Question

Until now we used JBoss AS 7.1 which has a tomcat as front-server. We now upgraded to Wildfly (JBoss 8.0) which ships with undertow as a tomcat replacement.

For our filedownloads, we are reading the input stream of the file, and writing this to the external context's response output stream. This worked well in JBoss AS 7.1 - even for large files. In Undertow WE receive the following exception even for pretty "small" files:

13:04:43,292 ERROR [io.undertow.request] (default task-15) Blocking request failed HttpServerExchange{ GET /project/getFile.xhtml}: java.lang.RuntimeException: org.xnio.channels.FixedLengthOverflowException
    at io.undertow.servlet.spec.HttpServletResponseImpl.responseDone(HttpServletResponseImpl.java:527)
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:287)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:227)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:73)
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:146)
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:168)
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:687)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_51]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_51]
    at java.lang.Thread.run(Thread.java:744) [rt.jar:1.7.0_51]
Caused by: org.xnio.channels.FixedLengthOverflowException
    at io.undertow.conduits.AbstractFixedLengthStreamSinkConduit.write(AbstractFixedLengthStreamSinkConduit.java:97)
    at org.xnio.conduits.Conduits.writeFinalBasic(Conduits.java:132) [xnio-api-3.2.0.Final.jar:3.2.0.Final]
    at io.undertow.conduits.AbstractFixedLengthStreamSinkConduit.writeFinal(AbstractFixedLengthStreamSinkConduit.java:137)
    at org.xnio.conduits.ConduitStreamSinkChannel.writeFinal(ConduitStreamSinkChannel.java:104) [xnio-api-3.2.0.Final.jar:3.2.0.Final]
    at io.undertow.channels.DetachableStreamSinkChannel.writeFinal(DetachableStreamSinkChannel.java:172)
    at io.undertow.servlet.spec.ServletOutputStreamImpl.writeBufferBlocking(ServletOutputStreamImpl.java:580)
    at io.undertow.servlet.spec.ServletOutputStreamImpl.close(ServletOutputStreamImpl.java:614)
    at io.undertow.servlet.spec.HttpServletResponseImpl.closeStreamAndWriter(HttpServletResponseImpl.java:451)
    at io.undertow.servlet.spec.HttpServletResponseImpl.responseDone(HttpServletResponseImpl.java:525)
    ... 9 more

The getFile.xhtml is invoking the download and the following code is used to copy the stream:

(Try, catch, logs and error handling removed to save some space)

public void downloadFile(FileEntity fileEntity) {
        FacesContext fc = FacesContext.getCurrentInstance();
        ExternalContext ec = fc.getExternalContext();

        ec.responseReset();
        ec.setResponseContentType(getMimeType(fileEntity.getFile()));
        ec.setResponseContentLength(new Long(fileEntity.getFile().length()).intValue());
        ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + ConversionHelper.validateFilename(fileEntity.getDisplayFileName()) + "\"");

        OutputStream output = ec.getResponseOutputStream();
        FileInputStream fis = new FileInputStream(fileEntity.getFile());

        IOUtils.copy(fis, output);

        fc.responseComplete();
    }

I noticed that removing the line

ec.setResponseContentLength(new Long(fileEntity.getFile().length()).intValue());

makes it work again. However the ResponseConentLength is "correct". Using

ec.setResponseContentLength(new Long(fileEntity.getFile().length()).intValue() + 9);

(note the +9) solves this. Using +8 -> FixedLengthOverflow, using +10 -> FixedLengthUnderflow

The +9 is independent of the filesize. Any idea? Looks strange to me...

Was it helpful?

Solution 2

Found the problem.

It has to do with the change of the server engine. Just not sure if it is directly related to undertow, or more an error with wildfly / JSF (or if it was an error in Jboss and now is working correctly):

To invoke the download, we used something like this in the getFile.xhtml:

    <f:metadata>
        <f:viewParam name="fileId" required="true"
            value="#{getFileController.fileId}"></f:viewParam>
    </f:metadata>

    <h:outputLabel value="#{getFileController.download()}" />

this would usually produce:

<label>value</label>

if the value of the label would be a string.

since we reset the response inside download(), <label> is removed again. Then, using a fixed response size that's equal the file-length and calling responseComplete() basically ommits the trailing </label>. Undertow however seems to ignore the responseComplete()-Call and tries to append </label> to the response, notices, that the end of the (fixed) response stream is reached, and therefore throws the mentioned exception.

That's why providing a size of +9 solves this error - but causes corrupt files, cause </label>\n will be appended to the file.

obviously the usage of an output label was bad practice there. But since we reseted and manually filled the response stream and called responseComplete() that was working fine.

the fix is obviously to not produce any (not required) html tags on that page:

    <f:metadata>
        <f:viewParam name="fileId" required="true"
            value="#{getFileController.fileId}"></f:viewParam>
        <f:event listener="#{getFileController.dlNow()}" type="preRenderView"></f:event>
    </f:metadata>

Beside the bad design there - shouldn't call responseComplete() ommit any additional writing to the response stream, including any write ATTEMPTS?

The docu says:

Signal the JavaServer Faces implementation that the HTTP response for this request has already been generated (such as an HTTP redirect), and that the request processing lifecycle should be terminated as soon as the current phase is completed.

So, if the Faces Implementation is told that the Response has been send - why would it try to append something?

OTHER TIPS

If you call output.close() it should have the effect you are after. This looks like an issue with JSF, and not related to Undertow.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top