Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions avaje-jex/src/main/java/io/avaje/jex/DJexConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ final class DJexConfig implements JexConfig {
private int bufferInitial = 256;
private long bufferMax = 4096L;
private int rangeChunkSize = 990_000;
private long maxRequestSize = 1_000_000L;
private HttpServerProvider serverProvider;

@Override
Expand Down Expand Up @@ -211,4 +212,15 @@ public JexConfig rangeChunkSize(int rangeChunkSize) {
this.rangeChunkSize = rangeChunkSize;
return this;
}

@Override
public JexConfig maxRequestSize(long maxRequestSize) {
this.maxRequestSize = maxRequestSize;
return this;
}

@Override
public long maxRequestSize() {
return maxRequestSize;
}
}
10 changes: 10 additions & 0 deletions avaje-jex/src/main/java/io/avaje/jex/JexConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ public interface JexConfig {
/** The configured rangeChunk size */
int rangeChunkSize();

/**
* Sets the the max size of request body that can be accessed without using using an InputStream
*
* @param maxRequestSize The size.
*/
JexConfig maxRequestSize(long maxRequestSize);

/** The configured maxRequestSize size */
long maxRequestSize();

/**
* Set the chunk size on range requests, set to a high number to reduce the amount of range
* requests (especially for video streaming)
Expand Down
11 changes: 10 additions & 1 deletion avaje-jex/src/main/java/io/avaje/jex/core/JdkContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import com.sun.net.httpserver.HttpsExchange;

import io.avaje.jex.http.Context;
import io.avaje.jex.http.HttpResponseException;
import io.avaje.jex.http.HttpStatus;
import io.avaje.jex.http.RedirectException;
import io.avaje.jex.security.BasicAuthCredentials;
Expand Down Expand Up @@ -124,6 +125,14 @@ public String body() {
public byte[] bodyAsBytes() {
try {
if (bodyBytes == null) {
var contentLength = contentLength();
long maxRequestSize = mgr.maxRequestSize();
if (contentLength > maxRequestSize || contentLength < 0) {
throw new HttpResponseException(
HttpStatus.REQUEST_ENTITY_TOO_LARGE_413.status(),
"Body content length unknown or greater than max configured size (%s bytes)"
.formatted(maxRequestSize));
}
bodyBytes = exchange.getRequestBody().readAllBytes();
}
return bodyBytes;
Expand Down Expand Up @@ -157,7 +166,7 @@ private Charset characterEncoding() {
@Override
public long contentLength() {
final String len = header(Constants.CONTENT_LENGTH);
return len == null ? 0 : Long.parseLong(len);
return len == null ? -1 : Long.parseLong(len);
}

@Override
Expand Down
12 changes: 10 additions & 2 deletions avaje-jex/src/main/java/io/avaje/jex/core/ServiceManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ final class ServiceManager {
private final int bufferInitial;
private final long bufferMax;
private final int rangeChunks;
private final long maxRequestSize;

static ServiceManager create(Jex jex) {
return new Builder(jex).build();
Expand All @@ -52,7 +53,8 @@ static ServiceManager create(Jex jex) {
String scheme,
long bufferMax,
int bufferInitial,
int rangeChunks) {
int rangeChunks,
long maxRequestSize) {
this.compressionConfig = compressionConfig;
this.jsonService = jsonService;
this.exceptionHandler = manager;
Expand All @@ -61,6 +63,7 @@ static ServiceManager create(Jex jex) {
this.bufferInitial = bufferInitial;
this.bufferMax = bufferMax;
this.rangeChunks = rangeChunks;
this.maxRequestSize = maxRequestSize;
}

OutputStream createOutputStream(JdkContext jdkContext) {
Expand Down Expand Up @@ -169,6 +172,10 @@ String scheme() {
return scheme;
}

long maxRequestSize() {
return maxRequestSize;
}

private static final class Builder {

private final Jex jex;
Expand All @@ -186,7 +193,8 @@ ServiceManager build() {
jex.config().scheme(),
jex.config().maxStreamBufferSize(),
jex.config().initialStreamBufferSize(),
jex.config().rangeChunkSize());
jex.config().rangeChunkSize(),
jex.config().maxRequestSize());
}

JsonService initJsonService() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.avaje.jex.core;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.http.HttpResponse;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;

import io.avaje.jex.Jex;

class ContextRequestTooBigTest {

static final TestPair pair = init();

static TestPair init() {
final Jex app =
Jex.create().config(c -> c.maxRequestSize(5)).post("/", ctx -> ctx.text(ctx.body()));

return TestPair.create(app);
}

@AfterAll
static void end() {
pair.shutdown();
}

@Test
void noBody() {
HttpResponse<String> res = pair.request().POST().asString();
assertThat(res.statusCode()).isEqualTo(200);
}

@Test
void overSized() {
HttpResponse<String> res = pair.request().body("amogus").POST().asString();
assertThat(res.statusCode()).isEqualTo(413);
}

@Test
void transferEncoding() throws IOException {
HttpURLConnection connection =
(HttpURLConnection) URI.create(pair.url()).toURL().openConnection();

connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setChunkedStreamingMode(2);

try (OutputStream os = connection.getOutputStream()) {
os.write("hi".getBytes());
}

assertThat(connection.getResponseCode()).isEqualTo(413);
}
}