Permalink
Browse files

Set limit to upload content

  • Loading branch information...
1 parent 3ee7022 commit 9ea53188a7b64ed34d704cd6246999021f6e8650 @pepite committed Feb 28, 2010
View
@@ -0,0 +1 @@
+play.module.netty.content.length.exceeded=HTTP content length exceeded %s bytes.
@@ -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.
@@ -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
*
@@ -14,15 +14,20 @@
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;
@@ -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;
@@ -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")
@@ -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;
@@ -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 {
@@ -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);
}
@@ -394,6 +433,7 @@ public static void serve404(NotFound e, ChannelHandlerContext ctx, Request reque
} catch (Exception ex) {
Logger.error(ex, "Error when getting Validation errors");
}
+
return binding;
}
@@ -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;
@@ -43,12 +46,15 @@
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
@@ -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();

0 comments on commit 9ea5318

Please sign in to comment.