blob: 6c2c88dbb6e8f73fea94fa8236d7eb3e1f492b2c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014, 2019 Raymond Augé and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Raymond Augé <raymond.auge@liferay.com> - Bug 436698
******************************************************************************/
package org.eclipse.equinox.http.servlet.internal.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.List;
import javax.servlet.*;
import javax.servlet.http.*;
import org.eclipse.equinox.http.servlet.internal.context.ContextController;
import org.eclipse.equinox.http.servlet.internal.context.DispatchTargets;
import org.eclipse.equinox.http.servlet.internal.registration.EndpointRegistration;
import org.eclipse.equinox.http.servlet.internal.registration.FilterRegistration;
import org.eclipse.equinox.http.servlet.internal.util.HttpStatus;
/**
* @author Raymond Augé
*/
public class ResponseStateHandler {
public ResponseStateHandler(
HttpServletRequest request, HttpServletResponse response,
DispatchTargets dispatchTargets) {
this.request = request;
this.response = response;
this.dispatchTargets = dispatchTargets;
}
public void processRequest() throws IOException, ServletException {
List<ServletRequestListener> servletRequestListeners = getServletRequestListener();
EndpointRegistration<?> endpoint = dispatchTargets.getServletRegistration();
List<FilterRegistration> filters = dispatchTargets.getMatchingFilterRegistrations();
endpoint.addReference();
for (FilterRegistration filterRegistration : filters) {
filterRegistration.addReference();
}
ServletRequestEvent servletRequestEvent = null;
try {
if ((dispatchTargets.getDispatcherType() == DispatcherType.REQUEST) && !servletRequestListeners.isEmpty()) {
servletRequestEvent = new ServletRequestEvent(endpoint.getServletContext(), request);
for (ServletRequestListener servletRequestListener : servletRequestListeners) {
servletRequestListener.requestInitialized(servletRequestEvent);
}
}
if (endpoint.getServletContextHelper().handleSecurity(request, response)) {
try {
if (filters.isEmpty()) {
endpoint.service(request, response);
}
else {
Collections.sort(filters);
FilterChain chain = new FilterChainImpl(
filters, endpoint, dispatchTargets.getDispatcherType());
chain.doFilter(request, response);
}
}
finally {
endpoint.getServletContextHelper().finishSecurity(request, response);
}
}
}
catch (Exception e) {
if (!(e instanceof IOException) &&
!(e instanceof RuntimeException) &&
!(e instanceof ServletException)) {
e = new ServletException(e);
}
setException(e);
if (dispatchTargets.getDispatcherType() != DispatcherType.ERROR &&
dispatchTargets.getDispatcherType() != DispatcherType.REQUEST) {
throwException(e);
}
}
finally {
endpoint.removeReference();
for (FilterRegistration filterRegistration : filters) {
filterRegistration.removeReference();
}
if ((exception != null) && dispatchTargets.getDispatcherType() == DispatcherType.ERROR) {
// This was the error handler throwing an error.
// We have to handle this with a custom error page of our own.
PrintWriter writer = response.getWriter();
Integer status = (Integer)request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
String message = (String)request.getAttribute(RequestDispatcher.ERROR_MESSAGE);
if (message == null) {
message = exception.getMessage();
}
request.getServletContext().log(message, exception);
final HttpStatus httpStatus = HttpStatus.of(status);
writer.println("<!DOCTYPE html>"); //$NON-NLS-1$
writer.println("<html lang=\"en\"><head>"); //$NON-NLS-1$
writer.println("<meta charset=\"utf-8\" />"); //$NON-NLS-1$
writer.println("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />"); //$NON-NLS-1$
writer.println("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />"); //$NON-NLS-1$
writer.print("<title>"); //$NON-NLS-1$
writer.print(httpStatus.value());
writer.print(" - "); //$NON-NLS-1$
writer.print(httpStatus.description());
writer.println("</title></head>"); //$NON-NLS-1$
writer.print("<body><div><h1>"); //$NON-NLS-1$
writer.print(httpStatus.value());
writer.print(" <small>"); //$NON-NLS-1$
writer.print(httpStatus.description());
writer.println("</small></h1>"); //$NON-NLS-1$
writer.print("<p>"); //$NON-NLS-1$
writer.print(message);
writer.println("</p></div></body></html>"); //$NON-NLS-1$
}
if (dispatchTargets.getDispatcherType() == DispatcherType.FORWARD) {
response.flushBuffer();
HttpServletResponseWrapperImpl responseWrapper = HttpServletResponseWrapperImpl.findHttpRuntimeResponse(response);
if (responseWrapper != null) {
responseWrapper.setCompleted(true);
}
else {
try (PrintWriter writer = response.getWriter()) {
// just force a close
}
catch (IllegalStateException ise1) {
try (ServletOutputStream outputStream = response.getOutputStream()) {
// just force a close
}
catch (IllegalStateException | IOException ise2) {
// ignore
}
}
catch (IOException ioe) {
// ignore
}
}
}
if (dispatchTargets.getDispatcherType() == DispatcherType.REQUEST) {
handleErrors();
for (ServletRequestListener servletRequestListener : servletRequestListeners) {
servletRequestListener.requestDestroyed(servletRequestEvent);
}
}
}
}
public void setException(Exception exception) {
this.exception = exception;
}
private List<ServletRequestListener> getServletRequestListener() {
return dispatchTargets.getContextController().getEventListeners().get(ServletRequestListener.class);
}
private void handleErrors() throws IOException, ServletException {
if (exception != null) {
handleException();
}
else {
handleResponseCode();
}
}
private void handleException() throws IOException, ServletException {
if (!(response instanceof HttpServletResponseWrapper)) {
throw new IllegalStateException("Response isn't a wrapper"); //$NON-NLS-1$
}
HttpServletResponseWrapperImpl responseWrapper = HttpServletResponseWrapperImpl.findHttpRuntimeResponse(response);
if (responseWrapper == null) {
throw new IllegalStateException("Can't locate response impl"); //$NON-NLS-1$
}
HttpServletResponse wrappedResponse = (HttpServletResponse)responseWrapper.getResponse();
if (wrappedResponse.isCommitted()) {
throwException(exception);
}
ContextController contextController = dispatchTargets.getContextController();
Class<?> clazz = exception.getClass();
final String originalClassName = clazz.getName();
String className = originalClassName;
DispatchTargets errorDispatchTargets;
do {
errorDispatchTargets = contextController.getDispatchTargets(
className, null, null, null, null, null, Match.ERROR, null);
if (errorDispatchTargets != null) {
break;
}
clazz = clazz.getSuperclass();
className = clazz.getName();
}
while (Exception.class.isAssignableFrom(clazz));
if (errorDispatchTargets == null) {
throwException(exception);
}
HttpServletRequestWrapperImpl httpRuntimeRequest = HttpServletRequestWrapperImpl.findHttpRuntimeRequest(request);
try {
errorDispatchTargets.setDispatcherType(DispatcherType.ERROR);
httpRuntimeRequest.push(errorDispatchTargets);
HttpServletRequest wrapperRequest = new HttpServletRequestWrapper(request) {
@Override
public Object getAttribute(String attributeName) {
if (getDispatcherType() == DispatcherType.ERROR) {
if (attributeName.equals(RequestDispatcher.ERROR_EXCEPTION)) {
return exception;
} else if (attributeName.equals(RequestDispatcher.ERROR_EXCEPTION_TYPE)) {
return originalClassName;
} else if (attributeName.equals(RequestDispatcher.ERROR_MESSAGE)) {
return exception.getMessage();
} else if (attributeName.equals(RequestDispatcher.ERROR_REQUEST_URI)) {
return request.getRequestURI();
} else if (attributeName.equals(RequestDispatcher.ERROR_SERVLET_NAME)) {
return dispatchTargets.getServletRegistration().getName();
} else if (attributeName.equals(RequestDispatcher.ERROR_STATUS_CODE)) {
return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
}
}
return super.getAttribute(attributeName);
}
@Override
public DispatcherType getDispatcherType() {
return DispatcherType.ERROR;
}
};
HttpServletResponseWrapper wrapperResponse =
new HttpServletResponseWrapperImpl(wrappedResponse);
ResponseStateHandler responseStateHandler = new ResponseStateHandler(
wrapperRequest, wrapperResponse, errorDispatchTargets);
responseStateHandler.processRequest();
wrappedResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
finally {
httpRuntimeRequest.pop();
}
}
private void handleResponseCode() throws IOException, ServletException {
if (!(response instanceof HttpServletResponseWrapper)) {
throw new IllegalStateException("Response isn't a wrapper"); //$NON-NLS-1$
}
final HttpServletResponseWrapperImpl responseWrapper = HttpServletResponseWrapperImpl.findHttpRuntimeResponse(response);
if (responseWrapper == null) {
throw new IllegalStateException("Can't locate response impl"); //$NON-NLS-1$
}
final int status = responseWrapper.getStatus();
if (status < HttpServletResponse.SC_BAD_REQUEST) {
return;
}
if (status == -1) {
// There's nothing more we can do here.
return;
}
HttpServletResponse wrappedResponse = (HttpServletResponse)responseWrapper.getResponse();
if (wrappedResponse.isCommitted()) {
// the response is committed already, but we need to propagate the error code anyway
wrappedResponse.sendError(status, responseWrapper.getMessage());
// There's nothing more we can do here.
return;
}
ContextController contextController = dispatchTargets.getContextController();
DispatchTargets errorDispatchTargets = contextController.getDispatchTargets(
String.valueOf(status), null, null, null, null, null, Match.ERROR, null);
if (errorDispatchTargets == null) {
wrappedResponse.sendError(status, responseWrapper.getMessage());
return;
}
HttpServletRequestWrapperImpl httpRuntimeRequest = HttpServletRequestWrapperImpl.findHttpRuntimeRequest(request);
try {
errorDispatchTargets.setDispatcherType(DispatcherType.ERROR);
httpRuntimeRequest.push(errorDispatchTargets);
HttpServletRequest wrapperRequest = new HttpServletRequestWrapper(request) {
@Override
public Object getAttribute(String attributeName) {
if (getDispatcherType() == DispatcherType.ERROR) {
if (attributeName.equals(RequestDispatcher.ERROR_MESSAGE)) {
return responseWrapper.getMessage();
} else if (attributeName.equals(RequestDispatcher.ERROR_REQUEST_URI)) {
return request.getRequestURI();
} else if (attributeName.equals(RequestDispatcher.ERROR_SERVLET_NAME)) {
return dispatchTargets.getServletRegistration().getName();
} else if (attributeName.equals(RequestDispatcher.ERROR_STATUS_CODE)) {
return status;
}
}
return super.getAttribute(attributeName);
}
@Override
public DispatcherType getDispatcherType() {
return DispatcherType.ERROR;
}
};
HttpServletResponseWrapper wrapperResponse =
new HttpServletResponseWrapperImpl(wrappedResponse);
ResponseStateHandler responseStateHandler = new ResponseStateHandler(
wrapperRequest, wrapperResponse, errorDispatchTargets);
wrappedResponse.setStatus(status);
responseStateHandler.processRequest();
}
finally {
httpRuntimeRequest.pop();
}
}
private void throwException(Exception e)
throws IOException, ServletException {
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
}
else if (e instanceof IOException) {
throw (IOException)e;
}
else if (e instanceof ServletException) {
throw (ServletException)e;
}
}
DispatchTargets dispatchTargets;
Exception exception;
HttpServletRequest request;
HttpServletResponse response;
}