Skip to content

Commit 3d272f9

Browse files
committed
HTTP body strategy fix jooby-project#455
Today, every single HTTP body is backed by a temp file... this is great for large file uploads its too much for small POST/PUT requests. Next release will come with a max body size flag that if exceed it go to filesystem. Otherwise, will keep body in memory. server.http.RequestBufferSize = 1m Default buffer size will be 1m
1 parent e60a39e commit 3d272f9

File tree

9 files changed

+296
-71
lines changed

9 files changed

+296
-71
lines changed

jooby/src/main/java/org/jooby/Jooby.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4557,7 +4557,6 @@ private Config defaultConfig(final Config config) {
45574557
if (dateFormat != null) {
45584558
defs = defs.withValue("application.dateFormat", ConfigValueFactory.fromAnyRef(dateFormat));
45594559
}
4560-
45614560
return defs;
45624561
}
45634562

jooby/src/main/java/org/jooby/internal/BodyReferenceImpl.java

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@
1818
*/
1919
package org.jooby.internal;
2020

21+
import java.io.ByteArrayOutputStream;
2122
import java.io.File;
23+
import java.io.FileOutputStream;
2224
import java.io.IOException;
2325
import java.io.InputStream;
2426
import java.io.OutputStream;
2527
import java.nio.charset.Charset;
2628
import java.nio.file.Files;
2729

28-
import org.jooby.Err;
2930
import org.jooby.Parser;
30-
import org.jooby.Status;
3131

32-
import com.google.common.io.Closeables;
32+
import com.google.common.io.ByteStreams;
3333

3434
public class BodyReferenceImpl implements Parser.BodyReference {
3535

@@ -39,54 +39,63 @@ public class BodyReferenceImpl implements Parser.BodyReference {
3939

4040
private File file;
4141

42+
private byte[] bytes;
43+
4244
public BodyReferenceImpl(final long length, final Charset charset, final File file,
43-
final InputStream in) throws IOException {
45+
final InputStream in, final long bufferSize) throws IOException {
4446
this.length = length;
4547
this.charset = charset;
46-
if (length > 0) {
47-
this.file = writeTo(file, in);
48+
if (length < bufferSize) {
49+
bytes = toByteArray(in);
50+
} else {
51+
this.file = copy(file, in);
4852
}
4953
}
5054

51-
public BodyReferenceImpl() {
52-
}
53-
5455
@Override
5556
public long length() {
5657
return length;
5758
}
5859

5960
@Override
6061
public byte[] bytes() throws IOException {
61-
checkContent();
62-
return Files.readAllBytes(file.toPath());
62+
if (bytes == null) {
63+
return Files.readAllBytes(file.toPath());
64+
} else {
65+
return bytes;
66+
}
6367
}
6468

6569
@Override
6670
public String text() throws IOException {
67-
checkContent();
6871
return new String(bytes(), charset);
6972
}
7073

7174
@Override
7275
public void writeTo(final OutputStream output) throws IOException {
73-
Files.copy(file.toPath(), output);
76+
if (bytes == null) {
77+
Files.copy(file.toPath(), output);
78+
} else {
79+
output.write(bytes);
80+
}
81+
7482
}
7583

76-
private File writeTo(final File file, InputStream in) throws IOException {
77-
try {
78-
file.getParentFile().mkdirs();
79-
Files.copy(in, file.toPath());
80-
} finally {
81-
Closeables.closeQuietly(in);
82-
in = null;
83-
}
84+
private static byte[] toByteArray(final InputStream in) throws IOException {
85+
ByteArrayOutputStream out = new ByteArrayOutputStream();
86+
copy(in, out);
87+
return out.toByteArray();
88+
}
89+
90+
private static File copy(final File file, final InputStream in) throws IOException {
91+
file.getParentFile().mkdirs();
92+
copy(in, new FileOutputStream(file));
8493
return file;
8594
}
8695

87-
private void checkContent() {
88-
if (file == null) {
89-
throw new Err(Status.BAD_REQUEST);
96+
private static void copy(final InputStream in, final OutputStream out) throws IOException {
97+
try (InputStream src = in; OutputStream dest = out) {
98+
ByteStreams.copy(src, dest);
9099
}
91100
}
92101

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.jooby.internal;
2+
3+
import java.io.IOException;
4+
import java.io.OutputStream;
5+
6+
import org.jooby.Err;
7+
import org.jooby.Parser;
8+
import org.jooby.Status;
9+
10+
public class EmptyBodyReference implements Parser.BodyReference {
11+
12+
@Override
13+
public byte[] bytes() throws IOException {
14+
throw new Err(Status.BAD_REQUEST);
15+
}
16+
17+
@Override
18+
public String text() throws IOException {
19+
throw new Err(Status.BAD_REQUEST);
20+
}
21+
22+
@Override
23+
public long length() {
24+
return 0;
25+
}
26+
27+
@Override
28+
public void writeTo(final OutputStream output) throws Exception {
29+
throw new Err(Status.BAD_REQUEST);
30+
}
31+
32+
}

jooby/src/main/java/org/jooby/internal/MutantImpl.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.jooby.Err;
3131
import org.jooby.MediaType;
3232
import org.jooby.Mutant;
33+
import org.jooby.Parser;
3334
import org.jooby.Status;
3435
import org.jooby.internal.parser.ParserExecutor;
3536

@@ -115,8 +116,8 @@ public boolean isSet() {
115116
if (data instanceof ParamReferenceImpl) {
116117
return ((ParamReferenceImpl) data).size() > 0;
117118
}
118-
if (data instanceof BodyReferenceImpl) {
119-
return ((BodyReferenceImpl) data).length() > 0;
119+
if (data instanceof Parser.BodyReference) {
120+
return ((Parser.BodyReference) data).length() > 0;
120121
}
121122
return ((Map) data).size() > 0;
122123
}
@@ -125,7 +126,7 @@ private Tuple3<String, String, Status> md() {
125126
return Match(data).of(
126127
Case(instanceOf(ParamReferenceImpl.class),
127128
p -> Tuple.of(p.name(), p.type() + " '" + p.name() + "'", Status.BAD_REQUEST)),
128-
Case(instanceOf(BodyReferenceImpl.class),
129+
Case(instanceOf(Parser.BodyReference.class),
129130
Tuple.of("body", "body", Status.UNSUPPORTED_MEDIA_TYPE)),
130131
Case($(), Tuple.of("params", "parameters", Status.BAD_REQUEST)));
131132
}

jooby/src/main/java/org/jooby/internal/RequestImpl.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import com.google.common.collect.ImmutableMap;
5757
import com.google.inject.Injector;
5858
import com.google.inject.Key;
59+
import com.typesafe.config.Config;
5960

6061
import javaslang.control.Try;
6162

@@ -252,14 +253,17 @@ public Mutant body() throws Exception {
252253
if (!type.isAny() && (MediaType.form.matches(type) || MediaType.multipart.matches(type))) {
253254
return params();
254255
}
255-
File fbody = new File(
256-
require("application.tmpdir", File.class),
256+
Config conf = require(Config.class);
257+
258+
File fbody = new File(conf.getString("application.tmpdir"),
257259
Integer.toHexString(System.identityHashCode(this)));
258260
files.add(fbody);
259-
Parser.BodyReference body = new BodyReferenceImpl(length, charset(), fbody, req.in());
261+
int bufferSize = conf.getBytes("server.http.RequestBufferSize").intValue();
262+
Parser.BodyReference body = new BodyReferenceImpl(length, charset(), fbody, req.in(),
263+
bufferSize);
260264
return new MutantImpl(require(ParserExecutor.class), type(), body);
261265
}
262-
return new MutantImpl(require(ParserExecutor.class), type(), new BodyReferenceImpl());
266+
return new MutantImpl(require(ParserExecutor.class), type(), new EmptyBodyReference());
263267
}
264268

265269
@Override

jooby/src/main/java/org/jooby/internal/parser/ParserBuilder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.jooby.Parser.Callback;
2828
import org.jooby.Upload;
2929
import org.jooby.internal.BodyReferenceImpl;
30+
import org.jooby.internal.EmptyBodyReference;
3031
import org.jooby.internal.StrParamReferenceImpl;
3132
import org.jooby.internal.UploadParamReferenceImpl;
3233

@@ -64,6 +65,7 @@ private TypeLiteral<?> typeOf(final Object value) {
6465
@Override
6566
public Builder body(final Callback<Parser.BodyReference> callback) {
6667
strategies.put(TypeLiteral.get(BodyReferenceImpl.class), callback);
68+
strategies.put(TypeLiteral.get(EmptyBodyReference.class), callback);
6769
return this;
6870
}
6971

jooby/src/main/resources/org/jooby/jooby.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ server {
122122
# Max response buffer size
123123
ResponseBufferSize = 16k
124124

125+
# Max request body size to keep in memory
126+
RequestBufferSize = 1m
127+
128+
# Max request size total (body + header)
125129
MaxRequestSize = 200k
126130

127131
IdleTimeout = 0

0 commit comments

Comments
 (0)