Hello Chris,

> -----Ursprüngliche Nachricht-----
> Von: Christopher Schultz <ch...@christopherschultz.net>
> Gesendet: Donnerstag, 16. Juni 2022 05:38
> An: users@tomcat.apache.org
> Betreff: Re: AW: Filehandle left open when using sendfile
> 
> Thomas,
> 
> On 6/15/22 02:26, Thomas Hoffmann (Speed4Trade GmbH) wrote:
> > Hello Christopher,
> >
> >> -----Ursprüngliche Nachricht-----
> >> Von: Christopher Schultz <ch...@christopherschultz.net>
> >> Gesendet: Dienstag, 14. Juni 2022 20:26
> >> An: users@tomcat.apache.org
> >> Betreff: Re: Filehandle left open when using sendfile
> >>
> >> Thomas,
> >>
> >> On 6/14/22 13:52, Thomas Hoffmann (Speed4Trade GmbH) wrote:
> >>> Hello,
> >>> we are using Tomcat 10.0.16 under windows.
> >>> For sending files to the browser, we are using sendfile by setting
> >>> the
> >> attribute "org.apache.tomcat.sendfile.filename".
> >>> Streaming an image to the browser works well in this way.
> >>> But we observed that if the user tries to delete the file
> >>> afterwards, there is
> >> still a file-handle on this file.
> >>
> >> Which user? Some admin on the server, or the user using the browser?
> >> (Dumb question, I know, but tb and ff had a bug for a while where
> >> downloads would leave dangling file handles, disallowing deleting
> >> those files after they were downloaded.)
> >>
> >>> I took a look at NioEndpoint.java -->
> >>>
> >>
> https://github.com/apache/tomcat/blob/main/java/org/apache/tomcat/uti
> >> l
> >>> /net/NioEndpoint.java
> >>>
> >>> At line 869 there is a stream opened:
> >>> FileInputStream fis = new FileInputStream(f);
> >>>
> >>> However, there is no close in this method. Maybe this might cause
> >>> the
> >> open file-handle?
> >>
> >> Did you read the line above that?
> >>
> >>       @SuppressWarnings("resource") // Closed when channel is closed
> >>       FileInputStream fis = new FileInputStream(f);
> >>
> >> The channel itself is cleaned-up properly and, with it, the file handle.
> >>
> >>
> https://github.com/apache/tomcat/blob/69b9a3eeee41062dc1930782941bb
> >> aa1880e9281/java/org/apache/tomcat/util/net/NioEndpoint.java#L1226
> >>
> >> (At least, I *think* this is where the cleanup happens. I'm not very
> >> well- versed in the connection-handling code in Tomcat.)
> >>
> >>> We are using protocol="org.apache.coyote.http11.Http11NioProtocol"..
> >>> I
> >> think there are different implementations for sendfile-feature.
> >>> The open file-handle can be observer via ProcessExplorer from
> Microsoft.
> >>
> >> How long did you wait for the file handle to close?
> >>
> >> Are you able to attach a debugger and see that the object hasn't been
> >> cleaned-up?
> >>
> >> -chris
> >>
> >
> > Thanks for taking a look at it.
> > Sorry, if the setup was not described in detail. Maybe you can imagine an
> image library, hosted server-side.
> > The user can upload pics and after upload the user can also decide to
> delete the image.
> 
> Light bulb: are you using ImageIO?

ImageIO is not used. The pictures are saved as they are without modification.

> 
> There have been some well-known leaks in there over the years. It's also
> *very* easy to leak file handles if you aren't managing your resources
> appropriately.
> 
> Are you *sure* you have clean use of resources within your own code?
> 
> If you are using *any* of Java's javax.imageio code, is it possible to skip 
> that
> and re-try your test? Can you e.g. upload a plain text file (or even an image)
> and only stream the bytes from client -> disk without doing anything else?
> 
> > Requests:
> > 1) POST-Request with image upload. Server saves the image in the file
> system and returns the URL.
> > 2) GET-Request, the browser requests the URL of the uploaded picture.
> > Server is sending the file via sendfile
> > 3) POST-Request with command to delete the picture (maybe user doesn’t
> > like the pic). Tomcat tries to delete the file on the server side but
> > fails because of the left handle in step 2
> 
> Are you sure it's from step #2 and not step #1?

At first I was hunting the problem at step 1)
After a while I figured out, that it is indeed happening in 2)

The following sample reproduces the orphaned file handle under Windows:

<%@page import="java.io.*"%>
<%@page import="java.net.*"%>
<%@page import="org.apache.coyote.Constants"%>
<%
    String fileName = "c:\\temp\\test.png";
    File file = new File(fileName);
    long l = file.length();
    request.setAttribute(Constants.SENDFILE_FILENAME_ATTR, 
file.getAbsolutePath());
    request.setAttribute(Constants.SENDFILE_FILE_START_ATTR, 0L);
    request.setAttribute(Constants.SENDFILE_FILE_END_ATTR, l);
    response.setHeader("Content-Length", Long.toString(l));
    response.setContentType(URLConnection.guessContentTypeFromName(fileName));
    response.flushBuffer();
%>

> 
> > The handle stays for several minutes for sure.
> >
> > It is hard to debug because when stepping through, the file-handle is gone
> after processing the request.
> 
> That seems suspicious if you are saying that Tomcat is definitely leaving the
> file handle open.
> 
> > I think there are some if-statements whether the transmission is pending
> or finished.
> > The mentioned line in the NIOEndpoint is not reached. So the file handle
> must be left somewhere else. My first assumption was wrong.
> 
> Does the problem go away if you disable sendfile and stream the file
> directly?
> 
> -chris
> 

Problem goes away when I stream it manually via commons-io:
IOUtils.copy(is, response.getOutputStream());

The handle gets opened via this code within Http2AsyncUpgradeHandler.java:

    protected SendfileState processSendfile(SendfileData sendfile) {
        if (sendfile != null) {
            try {
                try (FileChannel channel = FileChannel.open(sendfile.path, 
StandardOpenOption.READ)) {
                    sendfile.mappedBuffer = channel.map(MapMode.READ_ONLY, 
sendfile.pos, sendfile.end - sendfile.pos);
                }
    ......

In the meantime I stumbled upon this bug-Report: 
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4715154 
So maybe the problem lies even deeper.
Similar description here: 
https://cemerick.com/blog/2006/08/30/memory-mapping-files-in-java-causes-problems.html
 

Some ppl suggest to use java.lang.ref.Cleaner or don’t use Memory-Mapped files 
under Windows.
I don’t know if there are other solutions.

Greetings,
Thomas

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to