You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by cz...@apache.org on 2023/08/16 08:17:36 UTC

[felix-dev] branch master updated: FELIX-6625 : Make request parameters available for multipart requests

This is an automated email from the ASF dual-hosted git repository.

cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git


The following commit(s) were added to refs/heads/master by this push:
     new a69a5af087 FELIX-6625 : Make request parameters available for multipart requests
a69a5af087 is described below

commit a69a5af087add85b25de9155e1723fe80c46eb33
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Wed Aug 16 10:17:28 2023 +0200

    FELIX-6625 : Make request parameters available for multipart requests
---
 .../internal/dispatch/ServletRequestWrapper.java   | 349 ++++++++++++---------
 1 file changed, 196 insertions(+), 153 deletions(-)

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 97202c18d6..0f2c0c6432 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
@@ -41,9 +41,11 @@ 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;
@@ -52,6 +54,7 @@ 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.commons.fileupload.servlet.ServletFileUpload;
 import org.apache.felix.http.base.internal.context.ExtServletContext;
 import org.apache.felix.http.base.internal.handler.HttpSessionWrapper;
 import org.osgi.framework.Bundle;
@@ -92,7 +95,9 @@ final class ServletRequestWrapper extends HttpServletRequestWrapper
     private final MultipartConfig multipartConfig;
     private final Bundle bundleForSecurityCheck;
 
-    private Collection<Part> parts;
+    private Collection<PartImpl> parts;
+
+    private Map<String, String[]> partsParameterMap;
 
     public ServletRequestWrapper(final HttpServletRequest req,
             final ExtServletContext servletContext,
@@ -408,86 +413,74 @@ final class ServletRequestWrapper extends HttpServletRequestWrapper
         return this.asyncSupported;
     }
 
+    private RequestContext getMultipartContext() {
+        final RequestContext multipartContext;
+        if (!POST_METHOD.equalsIgnoreCase(this.getMethod())) {
+            multipartContext = null;
+        } else {
+            multipartContext = new RequestContext() {
 
-    private Collection<Part> checkMultipart() throws IOException, ServletException
-    {
-        if ( parts == null )
-        {
-            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 InputStream getInputStream() throws IOException {
-                        return ServletRequestWrapper.this.getInputStream();
-                    }
+                @Override
+                public String getContentType() {
+                    return ServletRequestWrapper.this.getContentType();
+                }
 
-                    @Override
-                    public String getContentType() {
-                        return ServletRequestWrapper.this.getContentType();
-                    }
+                @Override
+                public int getContentLength() {
+                    return ServletRequestWrapper.this.getContentLength();
+                }
 
-                    @Override
-                    public int getContentLength() {
-                        return ServletRequestWrapper.this.getContentLength();
-                    }
+                @Override
+                public String getCharacterEncoding() {
+                    return ServletRequestWrapper.this.getCharacterEncoding();
+                }
+            };
+        }
+        return multipartContext;
+    }
 
-                    @Override
-                    public String getCharacterEncoding() {
-                        return ServletRequestWrapper.this.getCharacterEncoding();
-                    }
-                };
-            }
-            if ( multipartContext != null && FileUploadBase.isMultipartContent(multipartContext) )
-            {
-                if ( this.multipartConfig == null)
-                {
+    private Collection<PartImpl> 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 )
-                {
+                if ( System.getSecurityManager() == null ) {
                     handleMultipart(multipartContext);
-                }
-                else
-                {
+                } else {
                     final AccessControlContext ctx = bundleForSecurityCheck.adapt(AccessControlContext.class);
-                    final IOException ioe = AccessController.doPrivileged(new PrivilegedAction<IOException>()
-                    {
+                    final IOException ioe = AccessController.doPrivileged(new PrivilegedAction<IOException>() {
 
                         @Override
-                        public IOException run()
-                        {
-                            try
-                            {
+                        public IOException run() {
+                            try {
                                 handleMultipart(multipartContext);
-                            }
-                            catch ( final IOException ioe)
-                            {
+                            } catch ( final IOException ioe) {
                                 return ioe;
                             }
                             return null;
                         }
                     }, ctx);
-                    if ( ioe != null )
-                    {
+                    if ( ioe != null ) {
                         throw ioe;
                     }
                 }
 
-            }
-            else
-            {
+            } else {
                 throw new ServletException("Not a multipart request");
             }
         }
         return parts;
     }
 
-    private void handleMultipart(final RequestContext multipartContext) throws IOException
-    {
+    private void handleMultipart(final RequestContext multipartContext) throws IOException {
         // Create a new file upload handler
         final FileUpload upload = new FileUpload();
         upload.setSizeMax(this.multipartConfig.multipartMaxRequestSize);
@@ -497,127 +490,177 @@ final class ServletRequestWrapper extends HttpServletRequestWrapper
         upload.setFileCountMax(this.multipartConfig.multipartMaxFileCount);
         // Parse the request
         List<FileItem> items = null;
-        try
-        {
+        try {
             items = upload.parseRequest(multipartContext);
-        }
-        catch (final FileUploadException fue)
-        {
+        } catch (final FileUploadException fue) {
             throw new IOException("Error parsing multipart request", fue);
         }
-        parts = new ArrayList<>();
-        for(final FileItem item : items)
-        {
-            parts.add(new Part() {
-
-                @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();
-                }
+        this.parts = new ArrayList<>();
+        for(final FileItem item : items) {
+            this.parts.add(new PartImpl(item));
+        }
+    }
 
-                @Override
-                public void write(String fileName) throws IOException
-                {
-                    try
-                    {
-                        item.write(new File(fileName));
-                    }
-                    catch (IOException e)
-                    {
-                        throw e;
-                    }
-                    catch (Exception e)
-                    {
-                        throw new IOException(e);
-                    }
-                }
+    @Override
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public Collection<Part> getParts() throws IOException, ServletException {
+        return (Collection)checkMultipart();
 
-                @Override
-                public void delete() throws IOException
-                {
-                    item.delete();
-                }
+    }
 
-                @Override
-                public String getHeader(String name)
-                {
-                    return item.getHeaders().getHeader(name);
-                }
+    @Override
+    public Part getPart(String name) throws IOException, ServletException {
+        Collection<PartImpl> parts = this.checkMultipart();
+        for(final Part p : parts) {
+            if ( p.getName().equals(name) ) {
+                return p;
+            }
+        }
+        return null;
+    }
 
-                @Override
-                public Collection<String> getHeaders(String name)
-                {
-                    final List<String> values = new ArrayList<>();
-                    final Iterator<String> iter = item.getHeaders().getHeaders(name);
-                    while ( iter.hasNext() )
-                    {
-                        values.add(iter.next());
+    private Map<String, String[]> getPartsParameterMap() {
+        if ( this.partsParameterMap == null ) {
+            try {
+                final Collection<PartImpl> parts = this.checkMultipart();
+                final Map<String, String[]> 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);
                     }
-                    return values;
                 }
+                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 Collection<String> getHeaderNames()
-                {
-                    final List<String> names = new ArrayList<>();
-                    final Iterator<String> iter = item.getHeaders().getHeaderNames();
-                    while ( iter.hasNext() )
-                    {
-                        names.add(iter.next());
-                    }
-                    return names;
-                }
-            });
+    @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 Collection<Part> getParts() throws IOException, ServletException
-    {
-        return checkMultipart();
+    public Map<String, String[]> getParameterMap() {
+        final RequestContext multipartContext = getMultipartContext();
+        if ( multipartContext != null && FileUploadBase.isMultipartContent(multipartContext) && this.multipartConfig != null) {
+            return this.getPartsParameterMap();
+        }
+        return super.getParameterMap();
+    }
 
+    @Override
+    public Enumeration<String> getParameterNames() {
+        final Map<String, String[]> params = this.getParameterMap();
+        return Collections.enumeration(params.keySet());
     }
 
     @Override
-    public Part getPart(String name) throws IOException, ServletException
-    {
-        Collection<Part> parts = this.checkMultipart();
-        for(final Part p : parts)
-        {
-            if ( p.getName().equals(name) )
-            {
-                return p;
-            }
-        }
-        return null;
+    public String[] getParameterValues(final String name) {
+        final Map<String, String[]> params = this.getParameterMap();
+        return params.get(name);
     }
 
     @Override
     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<String> getHeaders(final String name) {
+            final List<String> values = new ArrayList<>();
+            final Iterator<String> iter = item.getHeaders().getHeaders(name);
+            while ( iter.hasNext() ) {
+                values.add(iter.next());
+            }
+            return values;
+        }
+
+        @Override
+        public Collection<String> getHeaderNames() {
+            final List<String> names = new ArrayList<>();
+            final Iterator<String> iter = item.getHeaders().getHeaderNames();
+            while ( iter.hasNext() ) {
+                names.add(iter.next());
+            }
+            return names;
+        }
+
+        public FileItem getFileItem() {
+            return this.item;
+        }
+    }
 }