Skip to content

Commit

Permalink
Set limit to upload content
Browse files Browse the repository at this point in the history
  • Loading branch information
pepite committed Feb 28, 2010
1 parent 3ee7022 commit 9ea5318
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 25 deletions.
1 change: 1 addition & 0 deletions conf/messages
@@ -0,0 +1 @@
play.module.netty.content.length.exceeded=HTTP content length exceeded %s bytes.
17 changes: 17 additions & 0 deletions documentation/manual/home.textile
Expand Up @@ -17,3 +17,20 @@ h2. <a>Starting the Play! with netty as application server</a>

bc. play netty:run myapplication

h2. <a>Configuration</a>

The max upload size can be configured. In your application.conf add the following line:

bc. module.netty.maxContentLength=1048576

If the limit is exceeded, then an error message is set in the error scope. You can access it with the #{error} tag:

bc. #{errors}
<li>${error}</li>
#{/errors}

To change the default message, use the following message key:

play.module.netty.content.length.exceeded=My error

This property is currently set in the conf/messages directory of the netty module.
38 changes: 19 additions & 19 deletions src/play/modules/netty/FileChannelBuffer.java
@@ -1,22 +1,22 @@
/**
*
* Copyright 2010, Lunatech Labs.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
/**
*
* Copyright 2010, Lunatech Labs.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
* User: nicolas
* Date: Feb 25, 2010
*
Expand Down
9 changes: 7 additions & 2 deletions src/play/modules/netty/HttpServerPipelineFactory.java
Expand Up @@ -14,15 +14,20 @@ public class HttpServerPipelineFactory implements ChannelPipelineFactory {

public ChannelPipeline getPipeline() throws Exception {

Integer max = Integer.valueOf(Play.configuration.getProperty("module.netty.maxContentLength", "1048576"));
if (max == -1) {
max = Integer.MAX_VALUE;
}

ChannelPipeline pipeline = pipeline();

pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("encoder", new HttpResponseEncoder());


pipeline.addLast("streamer", new ChunkedWriteHandler());
pipeline.addLast("aggregator", new StreamChunkAggregator());
pipeline.addLast("aggregator", new StreamChunkAggregator(max));

pipeline.addLast("handler", new PlayHandler());

return pipeline;
Expand Down
46 changes: 43 additions & 3 deletions src/play/modules/netty/PlayHandler.java
Expand Up @@ -12,8 +12,11 @@
import play.Logger;
import play.Play;
import play.PlayPlugin;
import play.data.validation.Validation;
import play.data.validation.*;
import play.data.validation.Error;
import play.exceptions.PlayException;
import play.exceptions.UnexpectedException;
import play.i18n.Messages;
import play.libs.MimeTypes;
import play.mvc.ActionInvoker;
import play.mvc.Http;
Expand All @@ -29,9 +32,11 @@

import java.io.*;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.ParseException;
import java.util.*;
import java.util.regex.Matcher;


@ChannelPipelineCoverage("one")
Expand Down Expand Up @@ -132,10 +137,35 @@ public void run() {
@Override
public void execute() throws Exception {
ActionInvoker.invoke(request, response);
saveExceededSizeError(nettyRequest, response);
copyResponse(ctx, request, response, nettyRequest);
}
}

void saveExceededSizeError(HttpRequest nettyRequest, Response response) {

String warning = nettyRequest.getHeader(HttpHeaders.Names.WARNING);
String length = nettyRequest.getHeader(HttpHeaders.Names.CONTENT_LENGTH);
if (warning != null) {
try {
StringBuilder error = new StringBuilder();
error.append("\u0000");
error.append(warning);
error.append(":");
error.append(Messages.get(warning, length));
error.append("\u0000");
if (response.cookies.get(Scope.COOKIE_PREFIX + "_ERRORS") != null && response.cookies.get(Scope.COOKIE_PREFIX + "_ERRORS").value != null) {
error.append(response.cookies.get(Scope.COOKIE_PREFIX + "_ERRORS").value);
}
String errorData = URLEncoder.encode(error.toString(), "utf-8");
response.setCookie(Scope.COOKIE_PREFIX + "_ERRORS", errorData);
} catch (Exception e) {
throw new UnexpectedException("Flash serialization problem", e);
}
}
} // Thread

public static String COOKIE_PREFIX = Play.configuration.getProperty("application.session.cookie", "PLAY");

protected static void addToResponse(Response response, HttpResponse nettyResponse) {
Map<String, Http.Header> headers = response.headers;
Expand Down Expand Up @@ -183,7 +213,6 @@ public static void copyResponse(ChannelHandlerContext ctx, Request request, Resp
HttpResponse nettyResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(response.status));
nettyResponse.setHeader("Server", signature);


if (response.contentType != null) {
nettyResponse.setHeader("Content-Type", response.contentType + (response.contentType.startsWith("text/") && !response.contentType.contains("charset") ? "; charset=utf-8" : ""));
} else {
Expand Down Expand Up @@ -270,7 +299,17 @@ public static Request parseRequest(ChannelHandlerContext ctx, HttpRequest nettyR
ChannelBuffer b = nettyRequest.getContent();
if (b instanceof FileChannelBuffer) {
FileChannelBuffer buffer = (FileChannelBuffer) nettyRequest.getContent();
request.body = buffer.getInputStream();
// An error occured
Integer max = Integer.valueOf(Play.configuration.getProperty("module.netty.maxContentLength", "1048576"));
if (max == -1) {
max = Integer.MAX_VALUE;
}
if (buffer.getInputStream().available() < max) {
request.body = buffer.getInputStream();
} else {
request.body = new ByteArrayInputStream(new byte[0]);
}

} else {
request.body = new ChannelBufferInputStream(b);
}
Expand Down Expand Up @@ -394,6 +433,7 @@ protected static Map<String, Object> getBindingForErrors(Exception e, boolean is
} catch (Exception ex) {
Logger.error(ex, "Error when getting Validation errors");
}

return binding;
}

Expand Down
18 changes: 17 additions & 1 deletion src/play/modules/netty/StreamChunkAggregator.java
Expand Up @@ -29,11 +29,14 @@
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.frame.TooLongFrameException;
import org.jboss.netty.handler.codec.http.HttpChunk;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMessage;
import play.Logger;
import play.Play;
import play.mvc.Http;
import play.mvc.Scope;

import java.io.*;
import java.util.List;
Expand All @@ -43,12 +46,15 @@ public class StreamChunkAggregator extends SimpleChannelUpstreamHandler {

private volatile HttpMessage currentMessage;
private volatile String name;
private final int maxContentLength;


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

@Override
Expand Down Expand Up @@ -87,8 +93,18 @@ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
// Merge the received chunk into the content of the current message.
final HttpChunk chunk = (HttpChunk) msg;
final File file = new File(Play.tmpDir, name);

if (maxContentLength != -1 && (file.length() > maxContentLength - chunk.getContent().readableBytes())) {
currentMessage.setHeader(
HttpHeaders.Names.CONTENT_LENGTH, maxContentLength);
currentMessage.setHeader(
HttpHeaders.Names.WARNING, "play.module.netty.content.length.exceeded");
return;
}

final FileWriter fstream = new FileWriter(file, true);
final BufferedWriter out = new BufferedWriter(fstream);

IOUtils.copy(new ByteArrayInputStream(chunk.getContent().array()), out);
out.flush();
out.close();
Expand Down

0 comments on commit 9ea5318

Please sign in to comment.