Skip to content

Infinite loop on Content.copy() with Content.Source.from(... , Path, ) when Path has size 0. #13685

@pyraca

Description

@pyraca

Jetty version(s)
12.1.2

Jetty Environment
Jetty-12

HTTP version
N/A

Java version/vendor (use: java -version)
java version "21.0.2" 2024-01-16 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 21.0.2+13.1 (build 21.0.2+13-LTS-jvmci-23.1-b30)

OS type/version
Linux 6.8.0-79-generic #79-Ubuntu SMP PREEMPT_DYNAMIC Tue Aug 12 14:42:46 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux

Description

Content.copy(source,sink,cb) does not come back when a Content.Source.from(path) is given on a file that has 0 length (org.eclipse.jetty.io.internal.ByteChannelContentSource$PathContentSource).

The same is true when PathContentSource is used as the source (org.eclipse.jetty.io.content.PathContentSource).

The same does not happen on String or Byte sources.

When PathContentSource is a part on a MultiPart ContentSource the same issue happens.

Off by 1 someplace.

How to reproduce?

import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class PathContentSourceBug {

    static void p (String msg, Object ... args) {
        System.out.printf(msg,args);
        System.out.flush();
    }

    void ok () {
        p("Callback OK\n");
    }
    void fail(Throwable t) {
        p("Fault: \n");
        t.printStackTrace( System.out );
    }

    Content.Sink stdout () {
        return (last, byteBuffer, callback) -> {
            try
            {
                BufferUtil.writeTo(byteBuffer, System.out);
                callback.succeeded();
            }
            catch (Throwable t)
            {
                callback.failed(t);
            }
        };
    }

    void run(String[] args) throws IOException {
        Content.Sink sink = stdout();
        for(var next : args) {
            Path path = Paths.get(next);

            Content.Source source = Content.Source.from( path );
            p("Contents of: %s (size: %d bytes)\n", path, Files.size(path));
            Content.copy(source,sink,Callback.from(this::ok, this::fail));
            p("----------\n");
        }
    }

    public static void main(String[] args) throws IOException {
        new PathContentSourceBug().run(args);
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugFor general bugs on Jetty side

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions