SLING-11872 : Some request attributes not set when running with Felix Jetty 4.2.x
diff --git a/src/main/java/org/apache/sling/engine/impl/SlingRequestProcessorImpl.java b/src/main/java/org/apache/sling/engine/impl/SlingRequestProcessorImpl.java
index 38fd705..3c60588 100644
--- a/src/main/java/org/apache/sling/engine/impl/SlingRequestProcessorImpl.java
+++ b/src/main/java/org/apache/sling/engine/impl/SlingRequestProcessorImpl.java
@@ -30,6 +30,7 @@
 import java.util.Collections;
 import java.util.List;
 
+import javax.servlet.DispatcherType;
 import javax.servlet.FilterChain;
 import javax.servlet.Servlet;
 import javax.servlet.ServletContext;
@@ -62,6 +63,8 @@
 import org.apache.sling.engine.impl.filter.SlingComponentFilterChain;
 import org.apache.sling.engine.impl.parameters.ParameterSupport;
 import org.apache.sling.engine.impl.request.ContentData;
+import org.apache.sling.engine.impl.request.DispatcherRequestWrapper;
+import org.apache.sling.engine.impl.request.DispatchingInfo;
 import org.apache.sling.engine.impl.request.RequestData;
 import org.apache.sling.api.servlets.ErrorHandler;
 import org.osgi.service.component.annotations.Activate;
@@ -351,13 +354,12 @@
     public void dispatchRequest(final ServletRequest request,
             final ServletResponse response, final Resource resource,
             final RequestPathInfo resolvedURL, 
-            final boolean include,
-            final boolean protectHeadersOnInclude,
-            final boolean checkContentTypeOnInclude) throws IOException, ServletException {
+            final DispatchingInfo dispatchingInfo)
+    throws IOException, ServletException {
 
         // we need a SlingHttpServletRequest/SlingHttpServletResponse tupel
         // to continue
-        final SlingHttpServletRequest cRequest = RequestData.toSlingHttpServletRequest(request);
+        SlingHttpServletRequest cRequest = RequestData.toSlingHttpServletRequest(request);
         SlingHttpServletResponse cResponse = RequestData.toSlingHttpServletResponse(response);
 
         // get the request data (and btw check the correct type)
@@ -370,14 +372,15 @@
             Servlet servlet = servletResolver.resolveServlet(cRequest);
             contentData.setServlet(servlet);
 
-            FilterChainType type = include
+            cRequest = new DispatcherRequestWrapper(cRequest, dispatchingInfo);
+            final FilterChainType type = dispatchingInfo.getType() == DispatcherType.INCLUDE
                     ? FilterChainType.INCLUDE
                     : FilterChainType.FORWARD;
-            if (include) {
-                if (protectHeadersOnInclude) {
+            if (dispatchingInfo.getType() == DispatcherType.INCLUDE) {
+                if (dispatchingInfo.isProtectHeadersOnInclude()) {
                     cResponse = new IncludeResponseWrapper(cResponse);
                 }
-                if (checkContentTypeOnInclude) {
+                if (dispatchingInfo.isCheckContentTypeOnInclude()) {
                     cResponse = new IncludeNoContentTypeOverrideResponseWrapper(requestData, cResponse
                     );
                 }
diff --git a/src/main/java/org/apache/sling/engine/impl/request/DispatcherRequestWrapper.java b/src/main/java/org/apache/sling/engine/impl/request/DispatcherRequestWrapper.java
new file mode 100644
index 0000000..aa83734
--- /dev/null
+++ b/src/main/java/org/apache/sling/engine/impl/request/DispatcherRequestWrapper.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.sling.engine.impl.request;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.DispatcherType;
+
+import static javax.servlet.RequestDispatcher.FORWARD_CONTEXT_PATH;
+import static javax.servlet.RequestDispatcher.FORWARD_PATH_INFO;
+import static javax.servlet.RequestDispatcher.FORWARD_QUERY_STRING;
+import static javax.servlet.RequestDispatcher.FORWARD_REQUEST_URI;
+import static javax.servlet.RequestDispatcher.FORWARD_SERVLET_PATH;
+import static javax.servlet.RequestDispatcher.INCLUDE_CONTEXT_PATH;
+import static javax.servlet.RequestDispatcher.INCLUDE_PATH_INFO;
+import static javax.servlet.RequestDispatcher.INCLUDE_QUERY_STRING;
+import static javax.servlet.RequestDispatcher.INCLUDE_REQUEST_URI;
+import static javax.servlet.RequestDispatcher.INCLUDE_SERVLET_PATH;
+
+import org.apache.sling.api.SlingConstants;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper;
+
+public class DispatcherRequestWrapper extends SlingHttpServletRequestWrapper {
+
+    private static final List<String> FORWARD_ATTRIBUTES = Arrays.asList(FORWARD_CONTEXT_PATH,
+        FORWARD_PATH_INFO, FORWARD_QUERY_STRING, FORWARD_REQUEST_URI, FORWARD_SERVLET_PATH);
+
+    private static final List<String> INCLUDE_ATTRIBUTES = Arrays.asList(
+        SlingConstants.ATTR_REQUEST_CONTENT, SlingConstants.ATTR_REQUEST_SERVLET, SlingConstants.ATTR_REQUEST_PATH_INFO,
+        INCLUDE_CONTEXT_PATH, INCLUDE_PATH_INFO, INCLUDE_QUERY_STRING, INCLUDE_REQUEST_URI, INCLUDE_SERVLET_PATH);
+
+    private final DispatchingInfo dispatchingInfo;
+
+    public DispatcherRequestWrapper(final SlingHttpServletRequest request, final DispatchingInfo dispatchingInfo) {
+        super(request);
+        this.dispatchingInfo = dispatchingInfo;
+    }
+
+    @Override
+    public Object getAttribute(final String name) {
+        if (this.dispatchingInfo.getType() == DispatcherType.INCLUDE) {
+            if (SlingConstants.ATTR_REQUEST_CONTENT.equals(name)) {
+                return this.dispatchingInfo.getRequestContent();
+            } else if (SlingConstants.ATTR_REQUEST_SERVLET.equals(name)) {
+                return this.dispatchingInfo.getRequestServlet();
+            } else if (SlingConstants.ATTR_REQUEST_PATH_INFO.equals(name)) {
+                return this.dispatchingInfo.getRequestPathInfo();
+            } else if (INCLUDE_CONTEXT_PATH.equals(name)) {
+                return this.dispatchingInfo.getContextPath();
+            } else if (INCLUDE_PATH_INFO.equals(name)) {
+                return this.dispatchingInfo.getPathInfo();
+            } else if (INCLUDE_QUERY_STRING.equals(name)) {
+                return this.dispatchingInfo.getQueryString();
+            } else if (INCLUDE_REQUEST_URI.equals(name)) {
+                return this.dispatchingInfo.getRequestUri();
+            } else if (INCLUDE_SERVLET_PATH.equals(name)) {
+                return this.dispatchingInfo.getServletPath();
+            } else if (FORWARD_ATTRIBUTES.contains(name) ) {
+                // include might be contained within a forward, allow forward attributes
+                return super.getAttribute(name);
+            }
+        }
+        // block all special attributes
+        if (INCLUDE_ATTRIBUTES.contains(name) || FORWARD_ATTRIBUTES.contains(name)) {
+            return null;
+        }
+        return super.getAttribute(name);
+    }
+
+    @Override
+    public Enumeration<String> getAttributeNames() {
+        if ( this.dispatchingInfo.getType() == DispatcherType.INCLUDE ) {
+            final Set<String> allNames = new HashSet<>(Collections.list(super.getAttributeNames()));
+            allNames.addAll(INCLUDE_ATTRIBUTES);
+            return Collections.enumeration(allNames);
+        }
+        return super.getAttributeNames();
+    }
+}
diff --git a/src/main/java/org/apache/sling/engine/impl/request/DispatchingInfo.java b/src/main/java/org/apache/sling/engine/impl/request/DispatchingInfo.java
new file mode 100644
index 0000000..93f46bf
--- /dev/null
+++ b/src/main/java/org/apache/sling/engine/impl/request/DispatchingInfo.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sling.engine.impl.request;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Servlet;
+
+import org.apache.sling.api.request.RequestPathInfo;
+import org.apache.sling.api.resource.Resource;
+
+public class DispatchingInfo {
+
+    private final DispatcherType type;
+    private Resource requestContent;
+    private Servlet requestServlet;
+    private RequestPathInfo requestpathInfo;
+    private String contextPath;
+    private String pathInfo;
+    private String queryString;
+    private String requestUri;
+    private String servletPath;
+    private boolean protectHeadersOnInclude;
+    private boolean checkContentTypeOnInclude;
+
+    public DispatchingInfo(final DispatcherType type) {
+        this.type = type;
+    }
+
+    public DispatcherType getType() {
+        return type;
+    }
+
+    public Resource getRequestContent() {
+        return requestContent;
+    }
+
+    public void setRequestContent(final Resource requestContent) {
+        this.requestContent = requestContent;
+    }
+
+    public Servlet getRequestServlet() {
+        return requestServlet;
+    }
+
+    public void setRequestServlet(final Servlet requestServlet) {
+        this.requestServlet = requestServlet;
+    }
+
+    public RequestPathInfo getRequestPathInfo() {
+        return requestpathInfo;
+    }
+
+    public void setRequestPathInfo(final RequestPathInfo requestpathInfo) {
+        this.requestpathInfo = requestpathInfo;
+    }
+
+    public String getContextPath() {
+        return contextPath;
+    }
+
+    public void setContextPath(final String contextPath) {
+        this.contextPath = contextPath;
+    }
+
+    public String getPathInfo() {
+        return pathInfo;
+    }
+
+    public void setPathInfo(final String pathInfo) {
+        this.pathInfo = pathInfo;
+    }
+
+    public String getQueryString() {
+        return queryString;
+    }
+
+    public void setQueryString(final String queryString) {
+        this.queryString = queryString;
+    }
+
+    public String getRequestUri() {
+        return requestUri;
+    }
+
+    public void setRequestUri(final String requestUri) {
+        this.requestUri = requestUri;
+    }
+
+    public String getServletPath() {
+        return servletPath;
+    }
+
+    public void setServletPath(final String servletPath) {
+        this.servletPath = servletPath;
+    }
+
+    public boolean isProtectHeadersOnInclude() {
+        return protectHeadersOnInclude;
+    }
+
+    public void setProtectHeadersOnInclude(final boolean protectHeadersOnInclude) {
+        this.protectHeadersOnInclude = protectHeadersOnInclude;
+    }
+
+    public boolean isCheckContentTypeOnInclude() {
+        return checkContentTypeOnInclude;
+    }
+
+    public void setCheckContentTypeOnInclude(final boolean checkContentTypeOnInclude) {
+        this.checkContentTypeOnInclude = checkContentTypeOnInclude;
+    }
+}
diff --git a/src/main/java/org/apache/sling/engine/impl/request/SlingRequestDispatcher.java b/src/main/java/org/apache/sling/engine/impl/request/SlingRequestDispatcher.java
index 5006cfb..b0d5c48 100644
--- a/src/main/java/org/apache/sling/engine/impl/request/SlingRequestDispatcher.java
+++ b/src/main/java/org/apache/sling/engine/impl/request/SlingRequestDispatcher.java
@@ -20,6 +20,7 @@
 
 import java.io.IOException;
 
+import javax.servlet.DispatcherType;
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
@@ -27,7 +28,6 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.sling.api.SlingConstants;
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.request.RequestDispatcherOptions;
 import org.apache.sling.api.request.RequestProgressTracker;
@@ -89,42 +89,17 @@
         // code uses a ServletRequestWrapper rather than an
         // HttpServletRequestWrapper ...)
         final HttpServletRequest hRequest = (HttpServletRequest) request;
+        final DispatchingInfo info = new DispatchingInfo(DispatcherType.INCLUDE);
+        info.setRequestContent(cd.getResource());
+        info.setRequestServlet(cd.getServlet());
+        info.setRequestPathInfo(cd.getRequestPathInfo());
+        info.setContextPath(hRequest.getContextPath());
+        info.setPathInfo(hRequest.getPathInfo());
+        info.setQueryString(hRequest.getQueryString());
+        info.setRequestUri(hRequest.getRequestURI());
+        info.setServletPath(hRequest.getServletPath());
 
-        // set the inclusion request attributes from the current request
-        final Object v1 = setAttribute(request,
-            SlingConstants.ATTR_REQUEST_CONTENT, cd.getResource());
-        final Object v2 = setAttribute(request,
-            SlingConstants.ATTR_REQUEST_SERVLET, cd.getServlet());
-        final Object v3 = setAttribute(request,
-            SlingConstants.ATTR_REQUEST_PATH_INFO, cd.getRequestPathInfo());
-        final Object v4 = setAttribute(request,
-            SlingConstants.ATTR_INCLUDE_CONTEXT_PATH, hRequest.getContextPath());
-        final Object v5 = setAttribute(request,
-            SlingConstants.ATTR_INCLUDE_PATH_INFO, hRequest.getPathInfo());
-        final Object v6 = setAttribute(request,
-            SlingConstants.ATTR_INCLUDE_QUERY_STRING, hRequest.getQueryString());
-        final Object v7 = setAttribute(request,
-            SlingConstants.ATTR_INCLUDE_REQUEST_URI, hRequest.getRequestURI());
-        final Object v8 = setAttribute(request,
-            SlingConstants.ATTR_INCLUDE_SERVLET_PATH, hRequest.getServletPath());
-
-        try {
-
-            dispatch(request, sResponse, true);
-
-        } finally {
-
-            // reset inclusion request attributes to previous values
-            request.setAttribute(SlingConstants.ATTR_REQUEST_CONTENT, v1);
-            request.setAttribute(SlingConstants.ATTR_REQUEST_SERVLET, v2);
-            request.setAttribute(SlingConstants.ATTR_REQUEST_PATH_INFO, v3);
-            request.setAttribute(SlingConstants.ATTR_INCLUDE_CONTEXT_PATH, v4);
-            request.setAttribute(SlingConstants.ATTR_INCLUDE_PATH_INFO, v5);
-            request.setAttribute(SlingConstants.ATTR_INCLUDE_QUERY_STRING, v6);
-            request.setAttribute(SlingConstants.ATTR_INCLUDE_REQUEST_URI, v7);
-            request.setAttribute(SlingConstants.ATTR_INCLUDE_SERVLET_PATH, v8);
-
-        }
+        dispatch(request, sResponse, info);
     }
 
     @Override
@@ -142,17 +117,10 @@
         response.resetBuffer();
 
         // ensure inclusion information attributes are not set
-        request.removeAttribute(SlingConstants.ATTR_REQUEST_CONTENT);
-        request.removeAttribute(SlingConstants.ATTR_REQUEST_SERVLET);
-        request.removeAttribute(SlingConstants.ATTR_REQUEST_PATH_INFO);
-        request.removeAttribute(SlingConstants.ATTR_INCLUDE_CONTEXT_PATH);
-        request.removeAttribute(SlingConstants.ATTR_INCLUDE_PATH_INFO);
-        request.removeAttribute(SlingConstants.ATTR_INCLUDE_QUERY_STRING);
-        request.removeAttribute(SlingConstants.ATTR_INCLUDE_REQUEST_URI);
-        request.removeAttribute(SlingConstants.ATTR_INCLUDE_SERVLET_PATH);
+        final DispatchingInfo info = new DispatchingInfo(DispatcherType.FORWARD);
 
         // now just include as normal
-        dispatch(request, response, false);
+        dispatch(request, response, info);
 
         // finally, we would have to ensure the response is committed
         // and closed. Let's just flush the buffer and thus commit the
@@ -181,10 +149,10 @@
      * Dispatch the request
      * @param request The request
      * @param response The response
-     * @param include Is this an include (or forward)
+     * @param dispatchingInfo Is this an include (or forward)
      */
     private void dispatch(final ServletRequest request, final ServletResponse response,
-            final boolean include) throws ServletException, IOException {
+            final DispatchingInfo dispatchingInfo) throws ServletException, IOException {
         SlingHttpServletRequest cRequest = RequestData.unwrap(request);
         RequestData rd = RequestData.getRequestData(cRequest);
         String absPath = getAbsolutePath(cRequest, path);
@@ -223,11 +191,13 @@
         SlingRequestPathInfo info = getMergedRequestPathInfo(cRequest);
         requestProgressTracker.log(
             "Including resource {0} ({1})", resource, info);
-        boolean protectHeaders = this.options != null ?
+        final boolean protectHeaders = this.options != null ?
                 Boolean.parseBoolean(this.options.getOrDefault(RequestDispatcherOptions.OPT_PROTECT_HEADERS_ON_INCLUDE, String.valueOf(this.protectHeadersOnInclude)))
                 : this.protectHeadersOnInclude;
-        rd.getSlingRequestProcessor().dispatchRequest(request, response, resource,
-            info, include, protectHeaders, this.checkContentTypeOnInclude);
+        dispatchingInfo.setProtectHeadersOnInclude(protectHeaders);
+        dispatchingInfo.setCheckContentTypeOnInclude(this.checkContentTypeOnInclude);
+
+        rd.getSlingRequestProcessor().dispatchRequest(request, response, resource, info, dispatchingInfo);
     }
 
     /**
@@ -260,13 +230,6 @@
         return info;
     }
 
-    private Object setAttribute(final ServletRequest request,
-            final String name, final Object value) {
-        final Object oldValue = request.getAttribute(name);
-        request.setAttribute(name, value);
-        return oldValue;
-    }
-
     private static class TypeOverwritingResourceWrapper extends ResourceWrapper {
 
         private final String resourceType;