/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.esigate.servlet; import java.io.IOException; import java.util.Properties; import javax.servlet.ServletContext; import javax.servlet.ServletException; import org.apache.http.HttpStatus; import org.apache.http.client.methods.CloseableHttpResponse; import org.esigate.ConfigurationException; import org.esigate.Driver; import org.esigate.HttpErrorPage; import org.esigate.Parameters; import org.esigate.api.ContainerRequestContext; import org.esigate.events.Event; import org.esigate.events.EventDefinition; import org.esigate.events.EventManager; import org.esigate.events.IEventListener; import org.esigate.events.impl.FetchEvent; import org.esigate.extension.Extension; import org.esigate.http.IncomingRequest; import org.esigate.http.OutgoingRequest; import org.esigate.http.OutgoingRequestContext; import org.esigate.servlet.impl.ResponseCapturingWrapper; import org.esigate.servlet.impl.ResponseSender; import org.esigate.util.UriUtils; /** * Extension to enable local or cross-context includes inside a J2EE web application. * * This extension does not support background revalidation as it is not compliant with the Servlet request lifecycle. * * @author Francois-Xavier Bonnet * */ public class ServletExtension implements Extension, IEventListener { private Driver driver; private String context; private int maxObjectSize; private final ResponseSender responseSender = new ResponseSender(); @Override public void init(Driver driverParam, Properties properties) { this.driver = driverParam; driverParam.getEventManager().register(EventManager.EVENT_FETCH_PRE, this); context = properties.getProperty("context"); maxObjectSize = Parameters.MAX_OBJECT_SIZE.getValue(properties); if (!Parameters.STALE_WHILE_REVALIDATE.getDefaultValue().equals( Parameters.STALE_WHILE_REVALIDATE.getValue(properties))) { throw new ConfigurationException("ServletExtension does not support background revalidation (" + driver.getConfiguration().getInstanceName() + ".staleWhileRevalidate=" + Parameters.STALE_WHILE_REVALIDATE.getValue(properties) + ")"); } } @Override public boolean event(EventDefinition id, Event event) { FetchEvent fetchEvent = (FetchEvent) event; if (EventManager.EVENT_FETCH_PRE.equals(id)) { String uriString = fetchEvent.getHttpRequest().getRequestLine().getUri(); OutgoingRequest outgoingRequest = OutgoingRequestContext.adapt(fetchEvent.getHttpContext()).getOutgoingRequest(); IncomingRequest incomingRequest = outgoingRequest.getOriginalRequest().getOriginalRequest(); String baseUrl = outgoingRequest.getBaseUrl().toString(); if (outgoingRequest.getOriginalRequest().isExternal()) { // Non local absolute uri return true; } else { String relUrl = uriString; if (UriUtils.isAbsolute(relUrl)) { relUrl = relUrl.substring(UriUtils.extractHost(relUrl).toURI().length()); } relUrl = relUrl.substring(UriUtils.getPath(baseUrl).length()); if (!relUrl.startsWith("/")) { relUrl = "/" + relUrl; } ContainerRequestContext requestContext = outgoingRequest.getContainerRequestContext(); CloseableHttpResponse result; if (!(requestContext instanceof HttpServletRequestContext)) { String message = ServletExtension.class.getName() + " can be used only inside a java servlet engine"; result = HttpErrorPage.generateHttpResponse(HttpStatus.SC_BAD_GATEWAY, message); } else { HttpServletRequestContext httpServletRequestContext = (HttpServletRequestContext) requestContext; try { if (fetchEvent.getHttpContext().isProxy()) { ResponseCapturingWrapper wrappedResponse = new ResponseCapturingWrapper(httpServletRequestContext.getResponse(), driver.getContentTypeHelper(), true, maxObjectSize, responseSender, incomingRequest); if (context == null) { httpServletRequestContext.getFilterChain().doFilter( httpServletRequestContext.getRequest(), wrappedResponse); result = wrappedResponse.getCloseableHttpResponse(); } else { ServletContext crossContext = httpServletRequestContext.getServletContext().getContext(context); if (crossContext == null) { String message = "Context " + context + " does not exist or cross context disabled"; result = HttpErrorPage.generateHttpResponse(HttpStatus.SC_BAD_GATEWAY, message); } else { crossContext.getRequestDispatcher(relUrl).forward( httpServletRequestContext.getRequest(), wrappedResponse); result = wrappedResponse.getCloseableHttpResponse(); } } } else { ResponseCapturingWrapper wrappedResponse = new ResponseCapturingWrapper(httpServletRequestContext.getResponse(), driver.getContentTypeHelper(), false, maxObjectSize, responseSender, incomingRequest); if (context == null) { httpServletRequestContext.getRequest().getRequestDispatcher(relUrl) .forward(httpServletRequestContext.getRequest(), wrappedResponse); result = wrappedResponse.getCloseableHttpResponse(); } else { ServletContext crossContext = httpServletRequestContext.getServletContext().getContext(context); if (crossContext == null) { String message = "Context " + context + " does not exist or cross context disabled"; result = HttpErrorPage.generateHttpResponse(HttpStatus.SC_BAD_GATEWAY, message); } else { crossContext.getRequestDispatcher(relUrl).include( httpServletRequestContext.getRequest(), wrappedResponse); result = wrappedResponse.getCloseableHttpResponse(); } } } } catch (IOException | ServletException e) { result = HttpErrorPage.generateHttpResponse(e); } } fetchEvent.setHttpResponse(result); // Stop execution fetchEvent.setExit(true); } } return true; } }