diff --git a/docs/modules/ROOT/pages/using-tika/server/index.adoc b/docs/modules/ROOT/pages/using-tika/server/index.adoc index 743596ee47..113b2446fa 100644 --- a/docs/modules/ROOT/pages/using-tika/server/index.adoc +++ b/docs/modules/ROOT/pages/using-tika/server/index.adoc @@ -704,7 +704,7 @@ independent forked-process groups you run — see |`maxRequestSizeBytes` |`1 GiB` -|Maximum request body in bytes; larger requests are rejected with `413`. Enforced on bytes actually read, so a chunked upload with no `Content-Length` is bounded too. Bodies above xref:pipes/configuration.adoc#payload-limits[`maxInlineBytes`] are spooled to disk, so this also bounds how much one request can write. Raise it for larger documents, or set a negative value to disable the limit. +|Maximum request body in bytes; larger requests are rejected with `413`. Enforced on bytes actually read, so a chunked upload with no `Content-Length` is bounded too. Bodies above xref:pipes/configuration.adoc#payload-limits[`maxInlineBytes`] are spooled to disk, so this also bounds how much one request can write. Raise it for larger documents, or set a negative value to disable the limit. The server closes the connection while an oversized body is still arriving, so a client may see a connection reset instead of the `413`; Windows clients often do. |`maxRequestReadMillis` |`300000` (5 min) diff --git a/tika-parent/pom.xml b/tika-parent/pom.xml index d06ecd9109..279b067b6d 100644 --- a/tika-parent/pom.xml +++ b/tika-parent/pom.xml @@ -281,7 +281,7 @@ - 4.2.1-SNAPSHOT + 4.2.0-SNAPSHOT 17 17 17 diff --git a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/MaxRequestSizeIntegrationTest.java b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/MaxRequestSizeIntegrationTest.java index 918505f076..b3206510ac 100644 --- a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/MaxRequestSizeIntegrationTest.java +++ b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/MaxRequestSizeIntegrationTest.java @@ -29,6 +29,7 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; +import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.URI; import java.net.http.HttpClient; @@ -43,6 +44,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.GZIPOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -164,7 +166,8 @@ public void testEveryEndpointAnswersOverLimitChunkedBeforeBodyEnds() throws Exce if (status.startsWith("2")) { failures.add(String.join(" ", endpoint) + " " + e.getKey() + " -> " + status); } - anyTooLarge |= "413".equals(status); + // a reset may drop the 413 (Windows); it too proves the server stopped reading + anyTooLarge |= "413".equals(status) || "reset".equals(status); } if (!anyTooLarge) { failures.add(String.join(" ", endpoint) + " never reached the limit: " + statuses); @@ -560,14 +563,18 @@ private static String fillTemplate(String path) { return sb.toString(); } - /** Status code, "closed" if the connection closed first, or null if nothing came in time. */ + /** + * Status code, "closed" if the connection closed first, "reset" if it was reset before the + * whole body was sent, or null if nothing came in time or only after the whole body was read. + */ private String sendChunked(String[] endpoint, String contentType, byte[] preamble) - throws IOException { + throws IOException, InterruptedException { byte[] filler = new byte[64 * 1024]; Arrays.fill(filler, (byte) 'a'); try (Socket socket = new Socket("localhost", integrationTestPort)) { socket.setSoTimeout(10_000); OutputStream out = socket.getOutputStream(); + AtomicBoolean sentAll = new AtomicBoolean(); Thread writer = new Thread(() -> { try { out.write(requestHead(endpoint, contentType) @@ -577,6 +584,7 @@ private String sendChunked(String[] endpoint, String contentType, byte[] preambl writeChunk(out, filler); } out.flush(); + sentAll.set(true); // the terminating chunk is never sent } catch (IOException e) { // the server stopped reading and closed @@ -584,7 +592,12 @@ private String sendChunked(String[] endpoint, String contentType, byte[] preambl }); writer.setDaemon(true); writer.start(); - return readStatus(socket); + try { + return readStatus(socket); + } catch (SocketException e) { + writer.join(10_000); + return sentAll.get() ? null : "reset"; + } } }