/* * Copyright (C) 2012-2017 the original author or authors. * * 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 ninja.servlet; import static org.mockito.Mockito.when; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import ninja.Route; import ninja.bodyparser.BodyParserEngineManager; import ninja.params.ParamParser; import ninja.params.ParamParsers; import ninja.session.FlashScope; import ninja.session.Session; import ninja.uploads.FileItem; import ninja.uploads.FileItemProvider; import ninja.uploads.FileProvider; import ninja.uploads.MemoryFileItemProvider; import ninja.utils.NinjaMode; import ninja.utils.NinjaProperties; import ninja.utils.NinjaPropertiesImpl; import ninja.utils.ResultHandler; import ninja.validation.Validation; import org.apache.commons.fileupload.FileItemHeaders; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.io.IOUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; @RunWith(MockitoJUnitRunner.class) public class MultipartContextImplMemoryTest { @Mock private Session sessionCookie; @Mock private FlashScope flashCookie; @Mock private BodyParserEngineManager bodyParserEngineManager; @Mock private ServletContext servletContext; @Mock private HttpServletRequest httpServletRequest; @Mock private HttpServletResponse httpServletResponse; @Mock private ResultHandler resultHandler; @Mock private Validation validation; private NinjaProperties ninjaProperties; @Mock private NinjaServletContext context; private String paramA = "paramA"; private String paramB = "paramB"; private String valueA = "valueA"; private String valueB1 = "valueB1"; private String valueB2 = "valueB2"; private String file1 = "file1"; private String file2 = "file2"; private String file1Name = "my-file1.txt"; private String file2Name = "my-file2.txt"; private String file1Data = "abcdefghijklmnopqrstuvwxyz"; private String file2Data = "1234567890"; @Before public void setUp() throws IOException, ServletException { init(MemoryFileItemProvider.class); } public void init(final Class<? extends FileItemProvider> fileItemProviderClass) throws IOException, ServletException { //default setup for httpServlet request. //According to servlet spec the following will be returned: when(httpServletRequest.getContextPath()).thenReturn(""); when(httpServletRequest.getRequestURI()).thenReturn("/"); when(httpServletRequest.getContentType()).thenReturn("multipart/form-data"); when(httpServletRequest.getMethod()).thenReturn("POST"); NinjaPropertiesImpl properties = new NinjaPropertiesImpl(NinjaMode.test); this.ninjaProperties = properties; final FileItemIterator fileItemIterator = makeFileItemsIterator(); Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(NinjaProperties.class).toInstance(ninjaProperties); bind(FileItemProvider.class).to(fileItemProviderClass); } }); context = new NinjaServletContext( bodyParserEngineManager, flashCookie, ninjaProperties, resultHandler, sessionCookie, validation, injector, new ParamParsers(new HashSet<ParamParser>()) ) { public FileItemIterator getFileItemIterator() { return fileItemIterator; }; }; } @After public void tearDown() { context.cleanup(); } private FileItemIterator makeFileItemsIterator() { List<FileItemStream> fileItems = new ArrayList<>(); FileItemStreamImpl item; // ===== uploaded file items ===== item = new FileItemStreamImpl(file1, "text/plain", file1Name, file1Data.getBytes()); fileItems.add(item); item = new FileItemStreamImpl(file2, "text/plain", file2Name, file1Data.getBytes()); fileItems.add(item); item = new FileItemStreamImpl(file2, "text/plain", file2Name, file2Data.getBytes()); fileItems.add(item); // ===== simple key-value form fields ===== Map<String, List<String>> params = new HashMap<>(); params.put(paramA, Arrays.asList(valueA)); params.put(paramB, Arrays.asList(valueB1, valueB2)); return new FileItemIteratorImpl(fileItems, params); } @Test public void testGetParameter() { context.init(servletContext, httpServletRequest, httpServletResponse); Assert.assertEquals(valueA, context.getParameter(paramA)); Assert.assertEquals(valueB1, context.getParameter(paramB)); Assert.assertNull(context.getParameter("paramX")); } @Test public void testGetParameterValues() { context.init(servletContext, httpServletRequest, httpServletResponse); List<String> params = context.getParameterValues(paramA); Assert.assertEquals(1, params.size()); Assert.assertTrue(params.contains(valueA)); params = context.getParameterValues(paramB); Assert.assertEquals(2, params.size()); Assert.assertTrue(params.contains(valueB1)); Assert.assertTrue(params.contains(valueB2)); } @Test public void testGetParameters() { context.init(servletContext, httpServletRequest, httpServletResponse); Map<String, String[]> params = context.getParameters(); Assert.assertEquals(2, params.size()); String[] arr = params.get(paramA); Assert.assertNotNull(arr); Assert.assertEquals(1, arr.length); Assert.assertEquals(valueA, arr[0]); arr = params.get(paramB); Assert.assertNotNull(arr); Assert.assertEquals(2, arr.length); Assert.assertEquals(valueB1, arr[0]); Assert.assertEquals(valueB2, arr[1]); } @Test public void testIsMultipart() { context.init(servletContext, httpServletRequest, httpServletResponse); Assert.assertTrue(context.isMultipart()); } @Test public void testGetUploadedFileStream() throws IOException { context.init(servletContext, httpServletRequest, httpServletResponse); InputStream is = context.getParameterAsFileItem(file1).getInputStream(); Assert.assertNotNull(is); Assert.assertEquals(file1Data, IOUtils.toString(is)); Assert.assertNotNull(context.getParameterAsFileItem(file2)); Assert.assertNull(context.getParameterAsFileItem("fileX")); } @Test public void testGetUploadedFileStreams() { context.init(servletContext, httpServletRequest, httpServletResponse); List<FileItem> files = context.getParameterAsFileItems(file1); Assert.assertEquals(1, files.size()); // empty collection for nonexisting file files = context.getParameterAsFileItems("fileX"); Assert.assertNotNull(files); Assert.assertTrue(files.isEmpty()); } @Test public void testGetFileItems() { context.init(servletContext, httpServletRequest, httpServletResponse); Assert.assertEquals(2, context.getParameterFileItems().size()); int count = 0; for (List<FileItem> values : context.getParameterFileItems().values()) { count += values.size(); } Assert.assertEquals(3, count); } @Test public void testControllerWithFileProviderAtClass() throws Exception { context.init(servletContext, httpServletRequest, httpServletResponse); Class<ControllerImpl> controllerClass = ControllerImpl.class; Method controllerMethod = ControllerImpl.class.getMethod("method1"); Route route = new Route("GET", "/", controllerMethod, null); context.setRoute(route); Assert.assertEquals("test#1", context.getParameterAsFileItem(file1).getContentType()); } @Test public void testControllerWithFileProviderAtMethod() throws Exception { Class<ControllerImpl> controllerClass = ControllerImpl.class; Method controllerMethod = ControllerImpl.class.getMethod("method2"); Route route = new Route("GET", "/", controllerMethod, null); context.setRoute(route); context.init(servletContext, httpServletRequest, httpServletResponse); Assert.assertEquals("test#2", context.getParameterAsFileItem(file1).getContentType()); } /** * Simple file item stream impl to mock uploaded file items. */ private static class FileItemStreamImpl implements FileItemStream { String fieldName; String contentType; String name; byte[] data; boolean isFormField; public FileItemStreamImpl(String fieldName, byte[] data) { this.fieldName = fieldName; this.contentType = null; this.name = null; this.data = data; this.isFormField = true; } public FileItemStreamImpl(String fieldName, String contentType, String name, byte[] data) { this.fieldName = fieldName; this.contentType = contentType; this.name = name; this.data = data; this.isFormField = false; } @Override public InputStream openStream() throws IOException { return new ByteArrayInputStream(data); } @Override public String getContentType() { return contentType; } @Override public String getName() { return name; } @Override public String getFieldName() { return fieldName; } @Override public boolean isFormField() { return isFormField; } @Override public FileItemHeaders getHeaders() { return null; } @Override public void setHeaders(FileItemHeaders headers) { } } private class FileItemIteratorImpl implements FileItemIterator { private final List<FileItemStream> items; private int current = -1; public FileItemIteratorImpl(List<FileItemStream> fileItems, Map<String, List<String>> params) { // create list with uploaded file item streams this.items = new ArrayList<>(fileItems); // add form field params to the list for (Map.Entry<String, List<String>> e : params.entrySet()) { for (String value : e.getValue()) { this.items.add(new FileItemStreamImpl(e.getKey(), value.getBytes())); } } } @Override public boolean hasNext() throws FileUploadException, IOException { return current < items.size() - 1; } @Override public FileItemStream next() throws FileUploadException, IOException { return items.get(++current); } } @FileProvider(FileItemProviderImpl1.class) private class ControllerImpl { public void method1() { } @FileProvider(FileItemProviderImpl2.class) public void method2() { method1(); } } } class FileItemProviderImpl1 implements FileItemProvider { @Override public FileItem create(FileItemStream item) { return new FileItem() { @Override public String getFileName() { return null; } @Override public InputStream getInputStream() { return null; } @Override public File getFile() { return null; } @Override public String getContentType() { return "test#1"; } @Override public FileItemHeaders getHeaders() { return null; } @Override public void cleanup() { } }; } } class FileItemProviderImpl2 implements FileItemProvider { @Override public FileItem create(FileItemStream item) { return new FileItem() { @Override public String getFileName() { return null; } @Override public InputStream getInputStream() { return null; } @Override public File getFile() { return null; } @Override public String getContentType() { return "test#2"; } @Override public FileItemHeaders getHeaders() { return null; } @Override public void cleanup() { } }; } }