diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java index 68a8aea9a3..e33dfc7156 100644 --- a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java +++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java @@ -137,10 +137,24 @@ public void doFilter(final ServletRequest request, final ServletResponse respons final ExtServletContext servletContext = pr.handler.getContext(); final RequestInfo requestInfo = new RequestInfo(pr.servletPath, pr.pathInfo, null, req.getRequestURI(), pr.handler.getName(), pr.matchedPattern, pr.matchValue, pr.match, false); - final HttpServletRequest wrappedRequest = new ServletRequestWrapper(req, servletContext, requestInfo, null, - pr.handler.getServletInfo().isAsyncSupported(), - pr.handler.getMultipartConfig(), - pr.handler.getMultipartSecurityContext()); + + MultipartConfig multipartConfig = pr.handler.getMultipartConfig(); + HttpServletRequest wrappedRequest; + if(multipartConfig==null){ + wrappedRequest = new ServletRequestWrapper(req, + servletContext, + requestInfo, + null, + pr.handler.getServletInfo().isAsyncSupported()); + }else { + wrappedRequest = new ServletRequestMultipartWrapper(req, + servletContext, + requestInfo, + null, + pr.handler.getServletInfo().isAsyncSupported(), + multipartConfig, + pr.handler.getMultipartSecurityContext()); + } final FilterHandler[] filterHandlers = handlerRegistry.getFilters(pr, req.getDispatcherType(), pr.requestURI); try diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/RequestDispatcherImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/RequestDispatcherImpl.java index f95fef94ac..cf7d4dcb3f 100644 --- a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/RequestDispatcherImpl.java +++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/RequestDispatcherImpl.java @@ -67,13 +67,24 @@ public void forward(ServletRequest request, ServletResponse response) throws Ser try { - final ServletRequestWrapper req = new ServletRequestWrapper((HttpServletRequest) request, - this.resolution.handler.getContext(), - this.requestInfo, - DispatcherType.FORWARD, - this.resolution.handler.getServletInfo().isAsyncSupported(), - this.resolution.handler.getMultipartConfig(), - this.resolution.handler.getMultipartSecurityContext()); + MultipartConfig multipartConfig = this.resolution.handler.getMultipartConfig(); + ServletRequestWrapper req; + if (multipartConfig == null) { + req = new ServletRequestWrapper((HttpServletRequest) request, + this.resolution.handler.getContext(), + this.requestInfo, + DispatcherType.FORWARD, + this.resolution.handler.getServletInfo().isAsyncSupported()); + + } else { + req = new ServletRequestMultipartWrapper((HttpServletRequest) request, + this.resolution.handler.getContext(), + this.requestInfo, + DispatcherType.FORWARD, + this.resolution.handler.getServletInfo().isAsyncSupported(), + multipartConfig, + this.resolution.handler.getMultipartSecurityContext()); + } final String requestURI = UriUtils.concat(this.requestInfo.servletPath, this.requestInfo.pathInfo); final FilterHandler[] filterHandlers = this.resolution.handlerRegistry.getFilterHandlers(this.resolution.handler, DispatcherType.FORWARD, requestURI); @@ -104,13 +115,25 @@ public void forward(ServletRequest request, ServletResponse response) throws Ser @Override public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException { - final ServletRequestWrapper req = new ServletRequestWrapper((HttpServletRequest) request, - this.resolution.handler.getContext(), - this.requestInfo, - DispatcherType.INCLUDE, - this.resolution.handler.getServletInfo().isAsyncSupported(), - this.resolution.handler.getMultipartConfig(), - this.resolution.handler.getMultipartSecurityContext()); + MultipartConfig multipartConfig = this.resolution.handler.getMultipartConfig(); + ServletRequestWrapper req; + if (multipartConfig == null) { + req = new ServletRequestWrapper((HttpServletRequest) request, + this.resolution.handler.getContext(), + this.requestInfo, + DispatcherType.INCLUDE, + this.resolution.handler.getServletInfo().isAsyncSupported()); + } else { + req = new ServletRequestMultipartWrapper((HttpServletRequest) request, + this.resolution.handler.getContext(), + this.requestInfo, + DispatcherType.INCLUDE, + this.resolution.handler.getServletInfo().isAsyncSupported(), + multipartConfig, + this.resolution.handler.getMultipartSecurityContext()); + + } + final String requestURI = UriUtils.concat(this.requestInfo.servletPath, this.requestInfo.pathInfo); final FilterHandler[] filterHandlers = this.resolution.handlerRegistry.getFilterHandlers(this.resolution.handler, DispatcherType.INCLUDE, requestURI); diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletRequestMultipartWrapper.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletRequestMultipartWrapper.java new file mode 100644 index 0000000000..0f009c2cb6 --- /dev/null +++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletRequestMultipartWrapper.java @@ -0,0 +1,322 @@ +/* + * 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.felix.http.base.internal.dispatch; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileUpload; +import org.apache.commons.fileupload.FileUploadBase; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.fileupload.RequestContext; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.felix.http.base.internal.context.ExtServletContext; +import org.osgi.framework.Bundle; + +import jakarta.servlet.DispatcherType; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.Part; + +final class ServletRequestMultipartWrapper extends ServletRequestWrapper +{ + /** + * Constant for HTTP POST method. + */ + private static final String POST_METHOD = "POST"; + + private final MultipartConfig multipartConfig; + + private Collection parts; + + private Map partsParameterMap; + private Bundle bundleForSecurityCheck; + + public ServletRequestMultipartWrapper(final HttpServletRequest req, + final ExtServletContext servletContext, + final RequestInfo requestInfo, + final DispatcherType type, + final boolean asyncSupported, + final MultipartConfig multipartConfig, + final Bundle bundleForSecurityCheck) + { + super(req, servletContext, requestInfo, type, asyncSupported); + + this.multipartConfig = multipartConfig; + this.bundleForSecurityCheck = bundleForSecurityCheck; + + } + + private RequestContext getMultipartContext() { + final RequestContext multipartContext; + if (!POST_METHOD.equalsIgnoreCase(this.getMethod())) { + multipartContext = null; + } else { + multipartContext = new RequestContext() { + + @Override + public InputStream getInputStream() throws IOException { + return ServletRequestMultipartWrapper.this.getInputStream(); + } + + @Override + public String getContentType() { + return ServletRequestMultipartWrapper.this.getContentType(); + } + + @Override + public int getContentLength() { + return ServletRequestMultipartWrapper.this.getContentLength(); + } + + @Override + public String getCharacterEncoding() { + return ServletRequestMultipartWrapper.this.getCharacterEncoding(); + } + }; + } + return multipartContext; + } + + private Collection checkMultipart() throws IOException, ServletException { + if ( parts == null ) { + final RequestContext multipartContext = getMultipartContext(); + if ( multipartContext != null && FileUploadBase.isMultipartContent(multipartContext) ) { + if ( this.multipartConfig == null) { + throw new IllegalStateException("Multipart not enabled for servlet."); + } + + if ( System.getSecurityManager() == null ) { + handleMultipart(multipartContext); + } else { + final AccessControlContext ctx = bundleForSecurityCheck.adapt(AccessControlContext.class); + final IOException ioe = AccessController.doPrivileged(new PrivilegedAction() { + + @Override + public IOException run() { + try { + handleMultipart(multipartContext); + } catch ( final IOException ioe) { + return ioe; + } + return null; + } + }, ctx); + if ( ioe != null ) { + throw ioe; + } + } + + } else { + throw new ServletException("Not a multipart request"); + } + } + return parts; + } + + private void handleMultipart(final RequestContext multipartContext) throws IOException { + // Create a new file upload handler + final FileUpload upload = new FileUpload(); + upload.setSizeMax(this.multipartConfig.multipartMaxRequestSize); + upload.setFileSizeMax(this.multipartConfig.multipartMaxFileSize); + upload.setFileItemFactory(new DiskFileItemFactory(this.multipartConfig.multipartThreshold, + new File(this.multipartConfig.multipartLocation))); + upload.setFileCountMax(this.multipartConfig.multipartMaxFileCount); + // Parse the request + List items = null; + try { + items = upload.parseRequest(multipartContext); + } catch (final FileUploadException fue) { + throw new IOException("Error parsing multipart request", fue); + } + this.parts = new ArrayList<>(); + for(final FileItem item : items) { + this.parts.add(new PartImpl(item)); + } + } + + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Collection getParts() throws IOException, ServletException { + return (Collection)checkMultipart(); + + } + + @Override + public Part getPart(String name) throws IOException, ServletException { + Collection parts = this.checkMultipart(); + for(final Part p : parts) { + if ( p.getName().equals(name) ) { + return p; + } + } + return null; + } + + private Map getPartsParameterMap() { + if ( this.partsParameterMap == null ) { + try { + final Collection parts = this.checkMultipart(); + final Map params = new HashMap<>(); + for(final PartImpl p : parts) { + if (p.getFileItem().isFormField()) { + String[] current = params.get(p.getName()); + if (current == null) { + current = new String[] {p.getFileItem().getString()}; + } else { + String[] newCurrent = new String[current.length + 1]; + System.arraycopy( current, 0, newCurrent, 0, current.length ); + newCurrent[current.length] = p.getFileItem().getString(); + current = newCurrent; + } + params.put(p.getName(), current); + } + } + this.partsParameterMap = params; + } catch (final IOException | ServletException ignore) { + // ignore all exceptions and use default + } + if ( this.partsParameterMap == null ) { + // use map from container implementation as default + this.partsParameterMap = super.getParameterMap(); + } + } + return this.partsParameterMap; + } + + @Override + public String getParameter(final String name) { + final String[] values = this.getParameterValues(name); + if (values != null && values.length > 0) { + return values[0]; + } + return null; + } + + @Override + public Map getParameterMap() { + final RequestContext multipartContext = getMultipartContext(); + if ( multipartContext != null && FileUploadBase.isMultipartContent(multipartContext) && this.multipartConfig != null) { + return this.getPartsParameterMap(); + } + return super.getParameterMap(); + } + + @Override + public Enumeration getParameterNames() { + final Map params = this.getParameterMap(); + return Collections.enumeration(params.keySet()); + } + + @Override + public String[] getParameterValues(final String name) { + final Map params = this.getParameterMap(); + return params.get(name); + } + + private static final class PartImpl implements Part { + + private final FileItem item; + + public PartImpl(final FileItem item) { + this.item = item; + } + + @Override + public InputStream getInputStream() throws IOException { + return item.getInputStream(); + } + + @Override + public String getContentType() { + return item.getContentType(); + } + + @Override + public String getName() { + return item.getFieldName(); + } + + @Override + public String getSubmittedFileName() { + return item.getName(); + } + + @Override + public long getSize() { + return item.getSize(); + } + + @Override + public void write(final String fileName) throws IOException { + try { + item.write(new File(fileName)); + } catch (final IOException e) { + throw e; + } catch (final Exception e) { + throw new IOException(e); + } + } + + @Override + public void delete() throws IOException { + item.delete(); + } + + @Override + public String getHeader(final String name) { + return item.getHeaders().getHeader(name); + } + + @Override + public Collection getHeaders(final String name) { + final List values = new ArrayList<>(); + final Iterator iter = item.getHeaders().getHeaders(name); + while ( iter.hasNext() ) { + values.add(iter.next()); + } + return values; + } + + @Override + public Collection getHeaderNames() { + final List names = new ArrayList<>(); + final Iterator iter = item.getHeaders().getHeaderNames(); + while ( iter.hasNext() ) { + names.add(iter.next()); + } + return names; + } + + public FileItem getFileItem() { + return this.item; + } + } +} diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletRequestWrapper.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletRequestWrapper.java index 603c99b6c9..d47f305e69 100644 --- a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletRequestWrapper.java +++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletRequestWrapper.java @@ -30,33 +30,18 @@ import static jakarta.servlet.RequestDispatcher.INCLUDE_SERVLET_PATH; import static org.apache.felix.http.base.internal.util.UriUtils.concat; -import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; -import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.FileUpload; -import org.apache.commons.fileupload.FileUploadBase; -import org.apache.commons.fileupload.FileUploadException; -import org.apache.commons.fileupload.RequestContext; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.felix.http.base.internal.context.ExtServletContext; import org.apache.felix.http.base.internal.handler.HttpSessionWrapper; -import org.osgi.framework.Bundle; import org.osgi.service.servlet.context.ServletContextHelper; import org.osgi.service.useradmin.Authorization; @@ -74,46 +59,31 @@ import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.Part; -final class ServletRequestWrapper extends HttpServletRequestWrapper +class ServletRequestWrapper extends HttpServletRequestWrapper { - private static final List FORWARD_ATTRIBUTES = Arrays.asList(FORWARD_CONTEXT_PATH, + private static final List FORWARD_ATTRIBUTES = Arrays.asList(FORWARD_CONTEXT_PATH, FORWARD_MAPPING, FORWARD_PATH_INFO, FORWARD_QUERY_STRING, FORWARD_REQUEST_URI, FORWARD_SERVLET_PATH); - private static final List INCLUDE_ATTRIBUTES = Arrays.asList(INCLUDE_CONTEXT_PATH, + private static final List INCLUDE_ATTRIBUTES = Arrays.asList(INCLUDE_CONTEXT_PATH, INCLUDE_MAPPING, INCLUDE_PATH_INFO, INCLUDE_QUERY_STRING, INCLUDE_REQUEST_URI, INCLUDE_SERVLET_PATH); - /** - * Constant for HTTP POST method. - */ - private static final String POST_METHOD = "POST"; - - private final DispatcherType type; - private final RequestInfo requestInfo; + private final DispatcherType type; + private final RequestInfo requestInfo; private final ExtServletContext servletContext; private final boolean asyncSupported; - private final MultipartConfig multipartConfig; - private final Bundle bundleForSecurityCheck; - - private Collection parts; - - private Map partsParameterMap; public ServletRequestWrapper(final HttpServletRequest req, final ExtServletContext servletContext, final RequestInfo requestInfo, final DispatcherType type, - final boolean asyncSupported, - final MultipartConfig multipartConfig, - final Bundle bundleForSecurityCheck) + final boolean asyncSupported) { super(req); this.asyncSupported = asyncSupported; - this.multipartConfig = multipartConfig; this.servletContext = servletContext; this.requestInfo = requestInfo; this.type = type; - this.bundleForSecurityCheck = bundleForSecurityCheck; } @Override @@ -411,141 +381,15 @@ public boolean isAsyncSupported() return this.asyncSupported; } - private RequestContext getMultipartContext() { - final RequestContext multipartContext; - if (!POST_METHOD.equalsIgnoreCase(this.getMethod())) { - multipartContext = null; - } else { - multipartContext = new RequestContext() { - - @Override - public InputStream getInputStream() throws IOException { - return ServletRequestWrapper.this.getInputStream(); - } - - @Override - public String getContentType() { - return ServletRequestWrapper.this.getContentType(); - } - - @Override - public int getContentLength() { - return ServletRequestWrapper.this.getContentLength(); - } - - @Override - public String getCharacterEncoding() { - return ServletRequestWrapper.this.getCharacterEncoding(); - } - }; - } - return multipartContext; - } - - private Collection checkMultipart() throws IOException, ServletException { - if ( parts == null ) { - final RequestContext multipartContext = getMultipartContext(); - if ( multipartContext != null && FileUploadBase.isMultipartContent(multipartContext) ) { - if ( this.multipartConfig == null) { - throw new IllegalStateException("Multipart not enabled for servlet."); - } - - if ( System.getSecurityManager() == null ) { - handleMultipart(multipartContext); - } else { - final AccessControlContext ctx = bundleForSecurityCheck.adapt(AccessControlContext.class); - final IOException ioe = AccessController.doPrivileged(new PrivilegedAction() { - - @Override - public IOException run() { - try { - handleMultipart(multipartContext); - } catch ( final IOException ioe) { - return ioe; - } - return null; - } - }, ctx); - if ( ioe != null ) { - throw ioe; - } - } - - } else { - throw new ServletException("Not a multipart request"); - } - } - return parts; - } - - private void handleMultipart(final RequestContext multipartContext) throws IOException { - // Create a new file upload handler - final FileUpload upload = new FileUpload(); - upload.setSizeMax(this.multipartConfig.multipartMaxRequestSize); - upload.setFileSizeMax(this.multipartConfig.multipartMaxFileSize); - upload.setFileItemFactory(new DiskFileItemFactory(this.multipartConfig.multipartThreshold, - new File(this.multipartConfig.multipartLocation))); - upload.setFileCountMax(this.multipartConfig.multipartMaxFileCount); - // Parse the request - List items = null; - try { - items = upload.parseRequest(multipartContext); - } catch (final FileUploadException fue) { - throw new IOException("Error parsing multipart request", fue); - } - this.parts = new ArrayList<>(); - for(final FileItem item : items) { - this.parts.add(new PartImpl(item)); - } - } - @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public Collection getParts() throws IOException, ServletException { - return (Collection)checkMultipart(); - + throw new ServletException("No Multipart-Support available"); } @Override public Part getPart(String name) throws IOException, ServletException { - Collection parts = this.checkMultipart(); - for(final Part p : parts) { - if ( p.getName().equals(name) ) { - return p; - } - } - return null; - } - - private Map getPartsParameterMap() { - if ( this.partsParameterMap == null ) { - try { - final Collection parts = this.checkMultipart(); - final Map params = new HashMap<>(); - for(final PartImpl p : parts) { - if (p.getFileItem().isFormField()) { - String[] current = params.get(p.getName()); - if (current == null) { - current = new String[] {p.getFileItem().getString()}; - } else { - String[] newCurrent = new String[current.length + 1]; - System.arraycopy( current, 0, newCurrent, 0, current.length ); - newCurrent[current.length] = p.getFileItem().getString(); - current = newCurrent; - } - params.put(p.getName(), current); - } - } - this.partsParameterMap = params; - } catch (final IOException | ServletException ignore) { - // ignore all exceptions and use default - } - if ( this.partsParameterMap == null ) { - // use map from container implementation as default - this.partsParameterMap = super.getParameterMap(); - } - } - return this.partsParameterMap; + throw new ServletException("No Multipart-Support available"); } @Override @@ -559,11 +403,7 @@ public String getParameter(final String name) { @Override public Map getParameterMap() { - final RequestContext multipartContext = getMultipartContext(); - if ( multipartContext != null && FileUploadBase.isMultipartContent(multipartContext) && this.multipartConfig != null) { - return this.getPartsParameterMap(); - } - return super.getParameterMap(); + return super.getParameterMap(); } @Override @@ -583,82 +423,4 @@ public HttpServletMapping getHttpServletMapping() { return this.requestInfo; } - private static final class PartImpl implements Part { - - private final FileItem item; - - public PartImpl(final FileItem item) { - this.item = item; - } - - @Override - public InputStream getInputStream() throws IOException { - return item.getInputStream(); - } - - @Override - public String getContentType() { - return item.getContentType(); - } - - @Override - public String getName() { - return item.getFieldName(); - } - - @Override - public String getSubmittedFileName() { - return item.getName(); - } - - @Override - public long getSize() { - return item.getSize(); - } - - @Override - public void write(final String fileName) throws IOException { - try { - item.write(new File(fileName)); - } catch (final IOException e) { - throw e; - } catch (final Exception e) { - throw new IOException(e); - } - } - - @Override - public void delete() throws IOException { - item.delete(); - } - - @Override - public String getHeader(final String name) { - return item.getHeaders().getHeader(name); - } - - @Override - public Collection getHeaders(final String name) { - final List values = new ArrayList<>(); - final Iterator iter = item.getHeaders().getHeaders(name); - while ( iter.hasNext() ) { - values.add(iter.next()); - } - return values; - } - - @Override - public Collection getHeaderNames() { - final List names = new ArrayList<>(); - final Iterator iter = item.getHeaders().getHeaderNames(); - while ( iter.hasNext() ) { - names.add(iter.next()); - } - return names; - } - - public FileItem getFileItem() { - return this.item; - } - } } diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletResponseWrapper.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletResponseWrapper.java index cd1491d5cd..2f1748697b 100644 --- a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletResponseWrapper.java +++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletResponseWrapper.java @@ -114,9 +114,7 @@ public void sendError(final int code, final String message) throws IOException errorResolution.getContext(), requestInfo, DispatcherType.ERROR, - false, - null, - null); + false); final FilterChain filterChain = new InvocationChain(errorResolution, filterHandlers); filterChain.doFilter(reqWrapper, this);