Repository: helidon-io/helidon (java)

Issue:
MultiPart read data throw java.lang.ArrayIndexOutOfBoundsException
# MultiPart read data throw java.lang.ArrayIndexOutOfBoundsException

## Environment Details
* Helidon Version: 4.1.1
* Helidon SE or Helidon MP : SE
* JDK version: openjdk 23 x64
* OS: Windows 10 22H2
* Docker version (if applicable):

----------

## Problem Description
```java
public class Test {


    public static void main(String[] args) {
        WebServer.builder()
                .routing(routing -> {
                    routing.route(HttpRoute.builder()
                            .path("/")
                            .methods(Method.POST)
                            .handler((req, res) -> {
                                var multiPart = req.content().as(MultiPart.class);
                                if (multiPart != null) {
                                    while (multiPart.hasNext()) {
                                        var next = multiPart.next();
                                        if (next.fileName().isEmpty()) {
                                            System.out.println(next.as(String.class));
                                        } else {
                                            System.out.println(next.fileName().get());
                                            try (var inputStream = next.inputStream()){
                                                byte[] bytes = inputStream.readAllBytes();
                                                System.out.println(bytes.length);
                                            } catch (IOException e) {
                                                throw new RuntimeException(e);
                                            }
                                        }
                                    }
                                }
                                res.send("hello");
                            }));
                })
                .port(8990)
                .build()
                .start();

    }

}
```
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://localhost:8990" enctype="multipart/form-data"  method="POST">
    <input name="name" type="text">
    <input name="file" type="file">
    <input type="submit" >
</form>
</body>
</html>
```
# click submit will throw 
# there thread thing will happend 
```
1, it work and show hello 
2, some file will throw java.lang.ArrayIndexOutOfBoundsException (like create a docx and input some word)
3, some time will throw Self-suppression not permitted 
```

```
C:\Apps\jdk\bin\java.exe -javaagent:C:\Apps\idea\lib\idea_rt.jar=57012:C:\Apps\idea\bin -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath C:\Users\scx\Projects\scx\scx-http-helidon\target\test-classes;C:\Users\scx\Projects\scx\scx-http-helidon\target\classes;C:\Users\scx\Projects\scx\scx-http\target\classes;C:\Users\scx\Projects\scx\scx-common\target\classes;C:\Users\scx\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.17.2\jackson-core-2.17.2.jar;C:\Users\scx\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.17.2\jackson-datatype-jsr310-2.17.2.jar;C:\Users\scx\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.17.2\jackson-annotations-2.17.2.jar;C:\Users\scx\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.17.2\jackson-databind-2.17.2.jar;C:\Users\scx\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-xml\2.17.2\jackson-dataformat-xml-2.17.2.jar;C:\Users\scx\.m2\repository\org\codehaus\woodstox\stax2-api\4.2.2\stax2-api-4.2.2.jar;C:\Users\scx\.m2\repository\com\fasterxml\woodstox\woodstox-core\6.7.0\woodstox-core-6.7.0.jar;C:\Users\scx\.m2\repository\org\jasypt\jasypt\1.9.3\jasypt-1.9.3.jar;C:\Users\scx\Projects\scx\scx-reflect\target\classes;C:\Users\scx\.m2\repository\io\helidon\webserver\helidon-webserver\4.1.1\helidon-webserver-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\helidon\4.1.1\helidon-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common\4.1.1\helidon-common-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\http\helidon-http\4.1.1\helidon-http-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common-configurable\4.1.1\helidon-common-configurable-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common-mapper\4.1.1\helidon-common-mapper-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common-media-type\4.1.1\helidon-common-media-type-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common-uri\4.1.1\helidon-common-uri-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common-parameters\4.1.1\helidon-common-parameters-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common-socket\4.1.1\helidon-common-socket-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common-config\4.1.1\helidon-common-config-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common-context\4.1.1\helidon-common-context-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common-key-util\4.1.1\helidon-common-key-util-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common-security\4.1.1\helidon-common-security-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common-task\4.1.1\helidon-common-task-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common-tls\4.1.1\helidon-common-tls-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\logging\helidon-logging-common\4.1.1\helidon-logging-common-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\http\media\helidon-http-media\4.1.1\helidon-http-media-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\http\encoding\helidon-http-encoding\4.1.1\helidon-http-encoding-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\features\helidon-common-features\4.1.1\helidon-common-features-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\features\helidon-common-features-api\4.1.1\helidon-common-features-api-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\config\helidon-config\4.1.1\helidon-config-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\webserver\helidon-webserver-http2\4.1.1\helidon-webserver-http2-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\http\helidon-http-http2\4.1.1\helidon-http-http2-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\builder\helidon-builder-api\4.1.1\helidon-builder-api-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\http\media\helidon-http-media-multipart\4.1.1\helidon-http-media-multipart-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\webserver\helidon-webserver-websocket\4.1.1\helidon-webserver-websocket-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\websocket\helidon-websocket\4.1.1\helidon-websocket-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\webclient\helidon-webclient\4.1.1\helidon-webclient-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\webclient\helidon-webclient-api\4.1.1\helidon-webclient-api-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\webclient\helidon-webclient-http1\4.1.1\helidon-webclient-http1-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\webclient\helidon-webclient-websocket\4.1.1\helidon-webclient-websocket-4.1.1.jar;C:\Users\scx\.m2\repository\io\helidon\common\helidon-common-buffers\4.1.1\helidon-common-buffers-4.1.1.jar;C:\Users\scx\.m2\repository\org\testng\testng\7.10.2\testng-7.10.2.jar;C:\Users\scx\.m2\repository\org\slf4j\slf4j-api\2.0.16\slf4j-api-2.0.16.jar;C:\Users\scx\.m2\repository\com\beust\jcommander\1.82\jcommander-1.82.jar;C:\Users\scx\.m2\repository\org\webjars\jquery\3.7.1\jquery-3.7.1.jar cool.scx.http.helidon.test.Test
9月 26, 2024 11:42:22 上午 io.helidon.common.features.HelidonFeatures features
信息: Helidon SE 4.1.1 features: [Config, Encoding, Media, WebClient, WebServer, WebSocket]
9月 26, 2024 11:42:22 上午 io.helidon.webserver.ServerListener start
信息: [0x1c990c6f] http://0.0.0.0:8990 bound for socket '@default'
There is no Helidon logging implementation on classpath, skipping log configuration.
9月 26, 2024 11:42:23 上午 io.helidon.webserver.LoomServer startIt
信息: Started all channels in 37 milliseconds. 341 milliseconds since JVM startup. Java 23+37

������ Microsoft Word Document.docx
9月 26, 2024 11:42:29 上午 io.helidon.webserver.http.DirectHandlers handle
警告: Internal server error
io.helidon.http.RequestException: Index 1024 out of bounds for length 1024
	at io.helidon.http.RequestException$Builder.build(RequestException.java:139)
	at io.helidon.webserver.http.ErrorHandlers.unhandledError(ErrorHandlers.java:205)
	at io.helidon.webserver.http.ErrorHandlers.lambda$handleError$1(ErrorHandlers.java:184)
	at java.base/java.util.Optional.ifPresentOrElse(Optional.java:198)
	at io.helidon.webserver.http.ErrorHandlers.handleError(ErrorHandlers.java:183)
	at io.helidon.webserver.http.ErrorHandlers.runWithErrorHandling(ErrorHandlers.java:119)
	at io.helidon.webserver.http.Filters.filter(Filters.java:77)
	at io.helidon.webserver.http.HttpRoutingImpl.route(HttpRoutingImpl.java:73)
	at io.helidon.webserver.http1.Http1Connection.route(Http1Connection.java:425)
	at io.helidon.webserver.http1.Http1Connection.handle(Http1Connection.java:195)
	at io.helidon.webserver.ConnectionHandler.run(ConnectionHandler.java:166)
	at io.helidon.common.task.InterruptableTask.call(InterruptableTask.java:47)
	at io.helidon.webserver.ThreadPerTaskExecutor$ThreadBoundFuture.run(ThreadPerTaskExecutor.java:239)
	at java.base/java.lang.VirtualThread.run(VirtualThread.java:329)
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 1024 out of bounds for length 1024
	at io.helidon.common.buffers.Bytes.toWordLe(Bytes.java:163)
	at io.helidon.common.buffers.Bytes.toWord(Bytes.java:144)
	at io.helidon.common.buffers.Bytes.firstIndexOf(Bytes.java:122)
	at io.helidon.common.buffers.DataReader.findNewLine(DataReader.java:405)
	at io.helidon.http.media.multipart.ReadablePartNoLength$PartInputStream.readBuffer(ReadablePartNoLength.java:172)
	at io.helidon.http.media.multipart.ReadablePartNoLength$PartInputStream.ensureBuffer(ReadablePartNoLength.java:163)
	at io.helidon.http.media.multipart.ReadablePartNoLength$PartInputStream.read(ReadablePartNoLength.java:135)
	at java.base/java.io.InputStream.readNBytes(InputStream.java:412)
	at java.base/java.io.InputStream.readAllBytes(InputStream.java:349)
	at cool.scx.http.helidon.test.Test.lambda$main$0(Test.java:31)
	at io.helidon.webserver.http.HttpRoutingImpl$RoutingExecutor.doRoute(HttpRoutingImpl.java:165)
	at io.helidon.webserver.http.HttpRoutingImpl$RoutingExecutor.call(HttpRoutingImpl.java:124)
	at io.helidon.webserver.http.HttpRoutingImpl$RoutingExecutor.call(HttpRoutingImpl.java:102)
	at io.helidon.webserver.http.ErrorHandlers.runWithErrorHandling(ErrorHandlers.java:76)
	... 8 more
	Suppressed: java.lang.ArrayIndexOutOfBoundsException: Index 1024 out of bounds for length 1024
		at io.helidon.common.buffers.Bytes.toWordLe(Bytes.java:163)
		at io.helidon.common.buffers.Bytes.toWord(Bytes.java:144)
		at io.helidon.common.buffers.Bytes.firstIndexOf(Bytes.java:122)
		at io.helidon.common.buffers.DataReader.findNewLine(DataReader.java:405)
		at io.helidon.http.media.multipart.ReadablePartNoLength$PartInputStream.readBuffer(ReadablePartNoLength.java:172)
		at io.helidon.http.media.multipart.ReadablePartNoLength$PartInputStream.ensureBuffer(ReadablePartNoLength.java:163)
		at io.helidon.http.media.multipart.ReadablePartNoLength$PartInputStream.finish(ReadablePartNoLength.java:214)
		at io.helidon.http.media.multipart.ReadablePartNoLength$PartInputStream.close(ReadablePartNoLength.java:158)
		at cool.scx.http.helidon.test.Test.lambda$main$0(Test.java:30)
		... 12 more



```
## or
```

������ Microsoft Word Document.docx
9月 26, 2024 11:43:03 上午 io.helidon.webserver.http.DirectHandlers handle
警告: Internal server error
io.helidon.http.RequestException: Self-suppression not permitted
	at io.helidon.http.RequestException$Builder.build(RequestException.java:139)
	at io.helidon.webserver.http.ErrorHandlers.unhandledError(ErrorHandlers.java:205)
	at io.helidon.webserver.http.ErrorHandlers.lambda$handleError$1(ErrorHandlers.java:184)
	at java.base/java.util.Optional.ifPresentOrElse(Optional.java:198)
	at io.helidon.webserver.http.ErrorHandlers.handleError(ErrorHandlers.java:183)
	at io.helidon.webserver.http.ErrorHandlers.runWithErrorHandling(ErrorHandlers.java:119)
	at io.helidon.webserver.http.Filters.filter(Filters.java:77)
	at io.helidon.webserver.http.HttpRoutingImpl.route(HttpRoutingImpl.java:73)
	at io.helidon.webserver.http1.Http1Connection.route(Http1Connection.java:425)
	at io.helidon.webserver.http1.Http1Connection.handle(Http1Connection.java:195)
	at io.helidon.webserver.ConnectionHandler.run(ConnectionHandler.java:166)
	at io.helidon.common.task.InterruptableTask.call(InterruptableTask.java:47)
	at io.helidon.webserver.ThreadPerTaskExecutor$ThreadBoundFuture.run(ThreadPerTaskExecutor.java:239)
	at java.base/java.lang.VirtualThread.run(VirtualThread.java:329)
Caused by: java.lang.IllegalArgumentException: Self-suppression not permitted
	at java.base/java.lang.Throwable.addSuppressed(Throwable.java:1124)
	at cool.scx.http.helidon.test.Test.lambda$main$0(Test.java:30)
	at io.helidon.webserver.http.HttpRoutingImpl$RoutingExecutor.doRoute(HttpRoutingImpl.java:165)
	at io.helidon.webserver.http.HttpRoutingImpl$RoutingExecutor.call(HttpRoutingImpl.java:124)
	at io.helidon.webserver.http.HttpRoutingImpl$RoutingExecutor.call(HttpRoutingImpl.java:102)
	at io.helidon.webserver.http.ErrorHandlers.runWithErrorHandling(ErrorHandlers.java:76)
	... 8 more
Caused by: java.lang.ArrayIndexOutOfBoundsException

```





Pull request description:
4.1.2: Fix DataReader.findNewLine with lone EOL character (#9325)
Backport #9325 to Helidon 4.1.2

### Description

Add a missing check to keep the internal toIndex within bounds.

Fixes #9287

### Documentation

None

Patch:
diff --git a/common/buffers/src/main/java/io/helidon/common/buffers/DataReader.java b/common/buffers/src/main/java/io/helidon/common/buffers/DataReader.java
index f2402fb75c..312169be55 100644
--- a/common/buffers/src/main/java/io/helidon/common/buffers/DataReader.java
+++ b/common/buffers/src/main/java/io/helidon/common/buffers/DataReader.java
@@ -445,6 +445,9 @@ public class DataReader {
                     }
                     indexWithinNode = crIndex + 1;
                     idx += indexWithinNode;
+                    if (idx >= max) {
+                        return max;
+                    }
                     continue;
                 }
             }


Test patch:
diff --git a/common/buffers/src/test/java/io/helidon/common/buffers/DataReaderTest.java b/common/buffers/src/test/java/io/helidon/common/buffers/DataReaderTest.java
new file mode 100644
index 0000000000..0d88178d1d
--- /dev/null
+++ b/common/buffers/src/test/java/io/helidon/common/buffers/DataReaderTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.helidon.common.buffers;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+class DataReaderTest {
+
+    @Test
+    void testFindNewLineWithLoneCR() {
+        // reading N bytes at a time until a new line is found
+        // with data containing a lone CR
+
+        byte[] data = new byte[] {0, 0, (byte) '\r', 0, (byte) '\r', (byte) '\n'};
+        AtomicReference<byte[]> ref = new AtomicReference<>(data);
+        DataReader dataReader = new DataReader(() -> ref.getAndSet(null), true);
+
+        int n = 2;
+        assertThat(dataReader.findNewLine(n), is(n));
+        dataReader.skip(n);
+        assertThat(dataReader.findNewLine(n), is(n));
+        dataReader.skip(n);
+        assertThat(dataReader.findNewLine(n), is(0));
+    }
+}