in java/org/apache/catalina/connector/CoyoteAdapter.java [112:300]
public boolean asyncDispatch(org.apache.coyote.Request req, org.apache.coyote.Response res, SocketEvent status)
throws Exception {
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
if (request == null) {
throw new IllegalStateException(sm.getString("coyoteAdapter.nullRequest"));
}
boolean success = true;
AsyncContextImpl asyncConImpl = request.getAsyncContextInternal();
req.setRequestThread();
try {
if (!request.isAsync()) {
// Error or timeout
// Lift any suspension (e.g. if sendError() was used by an async
// request) to allow the response to be written to the client
response.setSuspended(false);
}
if (status == SocketEvent.TIMEOUT) {
if (!asyncConImpl.timeout()) {
asyncConImpl.setErrorState(null, false);
}
} else if (status == SocketEvent.ERROR) {
// An I/O error occurred on a non-container thread which means
// that the socket needs to be closed so set success as false to
// trigger a close
success = false;
Throwable t = (Throwable) req.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
Context context = request.getContext();
ClassLoader oldCL = null;
try {
oldCL = context.bind(null);
if (req.getReadListener() != null) {
req.getReadListener().onError(t);
}
if (res.getWriteListener() != null) {
res.getWriteListener().onError(t);
}
res.action(ActionCode.CLOSE_NOW, t);
asyncConImpl.setErrorState(t, true);
} finally {
context.unbind(oldCL);
}
}
// Check to see if non-blocking writes or reads are being used
if (!request.isAsyncDispatching() && request.isAsync()) {
WriteListener writeListener = res.getWriteListener();
ReadListener readListener = req.getReadListener();
if (writeListener != null && status == SocketEvent.OPEN_WRITE) {
Context context = request.getContext();
ClassLoader oldCL = null;
try {
oldCL = context.bind(null);
res.onWritePossible();
if (request.isFinished() && req.sendAllDataReadEvent() && readListener != null) {
readListener.onAllDataRead();
}
// User code may have swallowed an IOException
if (response.getCoyoteResponse().isExceptionPresent()) {
throw response.getCoyoteResponse().getErrorException();
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// Allow the error handling to write to the response
response.setSuspended(false);
// Need to trigger the call to AbstractProcessor.setErrorState()
// before the listener is called so the listener can call complete
// Therefore no need to set success=false as that would trigger a
// second call to AbstractProcessor.setErrorState()
// https://bz.apache.org/bugzilla/show_bug.cgi?id=65001
writeListener.onError(t);
res.action(ActionCode.CLOSE_NOW, t);
asyncConImpl.setErrorState(t, true);
} finally {
context.unbind(oldCL);
}
} else if (readListener != null && status == SocketEvent.OPEN_READ) {
Context context = request.getContext();
ClassLoader oldCL = null;
try {
oldCL = context.bind(null);
// If data is being read on a non-container thread a
// dispatch with status OPEN_READ will be used to get
// execution back on a container thread for the
// onAllDataRead() event. Therefore, make sure
// onDataAvailable() is not called in this case.
if (!request.isFinished()) {
req.onDataAvailable();
}
if (request.isFinished() && req.sendAllDataReadEvent()) {
readListener.onAllDataRead();
}
// User code may have swallowed an IOException
if (request.getCoyoteRequest().isExceptionPresent()) {
throw request.getCoyoteRequest().getErrorException();
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// Allow the error handling to write to the response
response.setSuspended(false);
// Need to trigger the call to AbstractProcessor.setErrorState()
// before the listener is called so the listener can call complete
// Therefore no need to set success=false as that would trigger a
// second call to AbstractProcessor.setErrorState()
// https://bz.apache.org/bugzilla/show_bug.cgi?id=65001
readListener.onError(t);
res.action(ActionCode.CLOSE_NOW, t);
asyncConImpl.setErrorState(t, true);
} finally {
context.unbind(oldCL);
}
}
}
// Has an error occurred during async processing that needs to be
// processed by the application's error page mechanism (or Tomcat's
// if the application doesn't define one)?
if (!request.isAsyncDispatching() && request.isAsync() && response.isErrorReportRequired()) {
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
}
if (request.isAsyncDispatching()) {
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
if (response.isError()) {
Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
asyncConImpl.setErrorState(t, true);
}
}
if (!request.isAsync()) {
request.finishRequest();
response.finishResponse();
}
// Check to see if the processor is in an error state. If it is,
// bail out now.
AtomicBoolean error = new AtomicBoolean(false);
res.action(ActionCode.IS_ERROR, error);
if (error.get()) {
if (request.isAsyncCompleting() || request.isAsyncDispatching()) {
// Connection will be forcibly closed which will prevent completion/dispatch happening at the usual
// point. Trigger post-processing here.
res.action(ActionCode.ASYNC_POST_PROCESS, null);
}
success = false;
}
} catch (IOException e) {
success = false;
// Ignore
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
success = false;
log.error(sm.getString("coyoteAdapter.asyncDispatch"), t);
} finally {
if (!success) {
res.setStatus(500);
}
// Access logging
if (!success || !request.isAsync()) {
long time = 0;
if (req.getStartTimeNanos() != -1) {
time = System.nanoTime() - req.getStartTimeNanos();
}
Context context = request.getContext();
if (context != null) {
context.logAccess(request, response, time, false);
} else {
log(req, res, time);
}
}
req.getRequestProcessor().setWorkerThreadName(null);
req.clearRequestThread();
// Recycle the wrapper request and response
if (!success || !request.isAsync()) {
updateWrapperErrorCount(request, response);
request.recycle();
response.recycle();
}
}
return success;
}