public static CompletionStage<List<MultipartFile>> read(HttpServerRequest r) {
        var contentType = r.headers().getFirst("content-type");
        if (contentType == null) {
            throw HttpServerResponseException.of(400, "content-type header is required");
        }
        var m = boundaryPattern.matcher(contentType);
        if (!m.matches()) {
            throw HttpServerResponseException.of(400, "content-type header is invalid");
        }
        var boundary = m.group("boundary");
        var future = new CompletableFuture<List<MultipartFile>>();
        var decoder = new MultipartDecoder(boundary, future);
        r.body().subscribe(decoder);
        return future;
    }
@Test
    void insomniaMultipart() {
        var body = """
            --X-INSOMNIA-BOUNDARY\r
            Content-Disposition: form-data; name="field1"\r
            Content-Type: text/plain\r
            \r
            value1\r
            --X-INSOMNIA-BOUNDARY\r
            Content-Disposition: form-data; name="field2"; filename="example.txt"\r
            Content-Type: text/plain\r
            \r
            value2\r
            --X-INSOMNIA-BOUNDARY--\r
            \r""".getBytes(StandardCharsets.UTF_8);
        var f = Flux.<ByteBuffer>create(sink -> {
            var i = 0;
            while (i < body.length) {
                var len = Math.min(ThreadLocalRandom.current().nextInt(10), body.length - i);
                var buf = ByteBuffer.wrap(body, i, len);
                sink.next(buf);
                i += len;
            }
            sink.complete();
        });

        var request = new SimpleHttpServerRequest("POST", "/", f, new Map.Entry[]{
            Map.entry("content-type", "multipart/form-data; boundary=X-INSOMNIA-BOUNDARY")
        }, Map.of());
        var result = MultipartReader.read(request)
            .toCompletableFuture().join();

        assertThat(result)
            .satisfies(part -> {
                assertThat(part.name()).isEqualTo("field1");
                assertThat(part.fileName()).isNull();
                assertThat(part.contentType()).isEqualTo("text/plain");
                assertThat(part.content()).asString(StandardCharsets.UTF_8).isEqualTo("value1");
            }, Index.atIndex(0))
            .satisfies(part -> {
                assertThat(part.name()).isEqualTo("field2");
                assertThat(part.fileName()).isEqualTo("example.txt");
                assertThat(part.contentType()).isEqualTo("text/plain");
                assertThat(part.content()).asString(StandardCharsets.UTF_8).isEqualTo("value2");
            }, Index.atIndex(1));
    }