2014-05-29 16:31 GMT+04:00 Mark Thomas <ma...@apache.org>:
> You've probably noticed I've been poking around the ErrorReportValve,
> the Response objects and other related code. I've been looking into an
> issue that one of the groups at $work has raised around Tomcat's error
> handling and I've been cleaning things up as I notice them to stop me
> getting distracted while I try and concentrate on the larger problem.
>
> I've also spotted a few places where I think we are duplicating
> functionality unnecessarily. I'm trying to clean this up as well. Partly
> because it is more efficient and partly because the code is easier to
> follow without it.
>
> So, that larger problem...
>
> It is to do with how uncaught exceptions are handled.
> ...
>

Reviewing r1600449 and followups.

// ErrorReportValve.java
[[[
        if (response.isCommitted()) {
            if (response.isErrorAfterCommit()) {
                // Flush any data that is still to be written to the client
                response.flushBuffer();
                // Close immediately to signal to the client that something went
                // wrong
                response.getCoyoteResponse().action(ActionCode.CLOSE_NOW, null);
            }
            return;
        }
]]]

1. If original error was due to I/O failure, then I expect
"response.flushBuffer()" call to fail with an IOException.

In ErrorReportValve#report() any IOException is silently swallowed. So
I think we can swallow it here.


// ErrorReportValve.java
[[[
        if (throwable != null) {
            // Make sure that the necessary methods have been called on the
            // response. (It is possible a component may just have set the
            // Throwable. Tomcat won't do that but other components might.)
            // These are safe to call at this point as we know that the response
            // has not been committed.
            response.reset();
            response.sendError(
HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }

        // One way or another, response.sendError() will have been called before
        // execution reaches this point and suspended the response. Need to
        // reverse that so this valve can write to the response.
        response.setSuspended(false);

        try {
            report(request, response, throwable);
        } catch (Throwable tt) {
            ExceptionUtils.handleThrowable(tt);
        }
]]]

I know that the above calls were essentially present before these
changes. Looking at them I have the following thoughts:

2. Looking at "if (throwable != null)".
The error might have already been handled either by custom error page
(StandardHostValve.custom(...)) or if there are several
ErrorReportValve valves in the chain.

In both cases the RequestDispatcher.ERROR_EXCEPTION attribute is not cleared.

The StandardHostValve saves us from this by always calling
response.flushBuffer();
just after invoking its custom() method. Thus "if
response.isCommitted()" branch is triggered and the error report valve
invocation returns early.

ErrorReportValve.report() can be updated to call flushBuffer() as
well. Otherwise I think if there are several ErrorReportValve valves
the "if throwable != null" branch will reset the response and it will
be generated a-new.

Though if there is no other good reason to flush, then it looks like a
hack (in StandardHostValve as well). (A known drawback of
flushBuffer() is that it causes chunked encoding with HTTP/1.1, even
if the whole response fits in a buffer).

I wonder if those flushBuffer() calls can be removed.


3. Some Tomcat components set RequestDispatcher.ERROR_EXCEPTION
attribute without setting any other error flags.

E.g. o.a.c.connector.Request#notifyAttributeAssigned),
#notifyAttributeRemoved().

I think this is the only use case where "if (throwable != null)"
branch and response.reset() call there are justified.

If response have been already committed the "if
response.isCommitted()" branch will be triggered, but not "if
response.isErrorAfterCommit()", as error flag has not been set.

So I interpret this use case as "Some error was detected, but it is
not fatal. If response have not been committed then report it. If it
has been committed, ignore it".


For 3. I think maybe do the following:
Replace "if (throwable != null)"
with "if (throwable != null && !response.isError())"

If the above replacement is done then I think that maybe those
flushBuffer() calls in StandardHostValve mentioned in "2." can be
removed.


Best regards,
Konstantin Kolinko

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

Reply via email to