/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.jersey.media.multipart.internal;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.media.multipart.BodyPart;
import org.glassfish.jersey.media.multipart.BodyPartEntity;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.glassfish.jersey.media.multipart.MultiPart;
import org.junit.Test;
import org.jvnet.mimepull.MIMEMessage;
import org.jvnet.mimepull.MIMEParsingException;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import mockit.Expectations;
import mockit.Mocked;
import mockit.Verifications;
/**
* Tests for multipart {@code MessageBodyReader} and {@code MessageBodyWriter} as well as {@code FormDataMultiPart} and {@code
* FormDataParam} injections.
*
* @author Paul Sandoz
* @author Michal Gajdos
*/
public class FormDataMultiPartReaderWriterTest extends MultiPartJerseyTest {
@Override
protected Set<Class<?>> getResourceClasses() {
return Arrays.asList(
ProducesFormDataUsingMultiPart.class,
ProducesFormDataResource.class,
ProducesFormDataCharsetResource.class,
ConsumesFormDataResource.class,
ConsumesFormDataParamResource.class,
FormDataTypesResource.class,
FormDataListTypesResource.class,
FormDataCollectionTypesResource.class,
PrimitivesFormDataParamResource.class,
DefaultFormDataParamResource.class,
NonContentTypeForPartResource.class,
MediaTypeWithBoundaryResource.class,
FileResource.class,
InputStreamResource.class).stream().collect(Collectors.toSet());
}
@Path("/ProducesFormDataUsingMultiPart")
public static class ProducesFormDataUsingMultiPart {
@GET
@Produces("multipart/form-data")
public Response get() {
final MultiPart entity = new MultiPart();
entity.setMediaType(MediaType.MULTIPART_FORM_DATA_TYPE);
final BodyPart part1 = new BodyPart();
part1.setMediaType(MediaType.TEXT_PLAIN_TYPE);
part1.getHeaders().add("Content-Disposition", "form-data; name=\"field1\"");
part1.setEntity("Joe Blow\r\n");
final BodyPart part2 = new BodyPart();
part2.setMediaType(MediaType.TEXT_PLAIN_TYPE);
part2.getHeaders().add("Content-Disposition", "form-data; name=\"pics\"; filename=\"file1.txt\"");
part2.setEntity("... contents of file1.txt ...\r\n");
return Response.ok(entity.bodyPart(part1).bodyPart(part2)).build();
}
}
// Test a response of type "multipart/form-data". The example comes from
// Section 6 of RFC 1867.
@Test
public void testProducesFormDataUsingMultiPart() {
final Invocation.Builder request = target().path("ProducesFormDataUsingMultiPart").request("multipart/form-data");
try {
final MultiPart result = request.get(MultiPart.class);
checkMediaType(new MediaType("multipart", "form-data"), result.getMediaType());
assertEquals(2, result.getBodyParts().size());
final BodyPart part1 = result.getBodyParts().get(0);
checkMediaType(new MediaType("text", "plain"), part1.getMediaType());
checkEntity("Joe Blow\r\n", (BodyPartEntity) part1.getEntity());
final String value1 = part1.getHeaders().getFirst("Content-Disposition");
assertEquals("form-data; name=\"field1\"", value1);
final BodyPart part2 = result.getBodyParts().get(1);
checkMediaType(new MediaType("text", "plain"), part2.getMediaType());
checkEntity("... contents of file1.txt ...\r\n", (BodyPartEntity) part2.getEntity());
final String value2 = part2.getHeaders().getFirst("Content-Disposition");
assertEquals("form-data; name=\"pics\"; filename=\"file1.txt\"", value2);
result.getParameterizedHeaders();
result.cleanup();
} catch (final IOException | ParseException e) {
e.printStackTrace(System.out);
fail("Caught exception: " + e);
}
}
@Path("/ProducesFormDataResource")
public static class ProducesFormDataResource {
// Test "multipart/form-data" the easy way (with subclasses)
@GET
@Produces("multipart/form-data")
public Response get() {
// Exercise builder pattern with explicit content type
final MultiPartBean bean = new MultiPartBean("myname", "myvalue");
return Response.ok(new FormDataMultiPart()
.field("foo", "bar")
.field("baz", "bop")
.field("bean", bean, new MediaType("x-application", "x-format"))).build();
}
}
@Test
public void testProducesFormDataResource() throws Exception {
final Invocation.Builder request = target().path("ProducesFormDataResource").request("multipart/form-data");
final FormDataMultiPart result = request.get(FormDataMultiPart.class);
checkMediaType(new MediaType("multipart", "form-data"), result.getMediaType());
assertEquals(3, result.getFields().size());
assertNotNull(result.getField("foo"));
assertEquals("bar", result.getField("foo").getValue());
assertNotNull(result.getField("baz"));
assertEquals("bop", result.getField("baz").getValue());
assertNotNull(result.getField("bean"));
final MultiPartBean bean = result.getField("bean").getValueAs(MultiPartBean.class);
assertNotNull(bean);
assertEquals("myname", bean.getName());
assertEquals("myvalue", bean.getValue());
result.cleanup();
}
@Path("/ProducesFormDataCharsetResource")
public static class ProducesFormDataCharsetResource {
// Test "multipart/form-data" the easy way (with subclasses)
@GET
@Produces("multipart/form-data")
public Response get(@QueryParam("charset") final String charset) {
return Response.ok(new FormDataMultiPart()
.field("foo", "\u00A9 CONTENT \u00FF \u2200 \u22FF",
MediaType.valueOf("text/plain;charset=" + charset))).build();
}
}
@Test
public void testProducesFormDataCharsetResource() throws Exception {
final String c = "\u00A9 CONTENT \u00FF \u2200 \u22FF";
for (final String charset : Arrays.asList(
"US-ASCII",
"ISO-8859-1",
"UTF-8",
"UTF-16BE",
"UTF-16LE",
"UTF-16")) {
final FormDataMultiPart p = target().path("ProducesFormDataCharsetResource")
.queryParam("charset", charset)
.request("multipart/form-data")
.get(FormDataMultiPart.class);
final String expected = new String(c.getBytes(charset), charset);
assertEquals(expected, p.getField("foo").getValue());
}
}
@Path("/ConsumesFormDataResource")
public static class ConsumesFormDataResource {
@PUT
@Consumes("multipart/form-data")
@Produces("text/plain")
public Response get(final FormDataMultiPart multiPart) throws IOException {
if (!(multiPart.getBodyParts().size() == 3)) {
return Response.ok("FAILED: Number of body parts is " + multiPart.getBodyParts().size() + " instead of 3")
.build();
}
if (multiPart.getField("foo") == null) {
return Response.ok("FAILED: Missing field 'foo'").build();
} else if (!"bar".equals(multiPart.getField("foo").getValue())) {
return Response
.ok("FAILED: Field 'foo' has value '" + multiPart.getField("foo").getValue() + "' instead of 'bar'")
.build();
}
if (multiPart.getField("baz") == null) {
return Response.ok("FAILED: Missing field 'baz'").build();
} else if (!"bop".equals(multiPart.getField("baz").getValue())) {
return Response
.ok("FAILED: Field 'baz' has value '" + multiPart.getField("baz").getValue() + "' instead of 'bop'")
.build();
}
if (multiPart.getField("bean") == null) {
return Response.ok("FAILED: Missing field 'bean'").build();
}
final MultiPartBean bean = multiPart.getField("bean").getValueAs(MultiPartBean.class);
if (!bean.getName().equals("myname")) {
return Response.ok("FAILED: Second part name = " + bean.getName()).build();
}
if (!bean.getValue().equals("myvalue")) {
return Response.ok("FAILED: Second part value = " + bean.getValue()).build();
}
return Response.ok("SUCCESS: All tests passed").build();
}
}
@Test
public void testConsumesFormDataResource() {
final Invocation.Builder request = target().path("ConsumesFormDataResource").request("text/plain");
final MultiPartBean bean = new MultiPartBean("myname", "myvalue");
final FormDataMultiPart entity = new FormDataMultiPart()
.field("foo", "bar")
.field("baz", "bop")
.field("bean", bean, new MediaType("x-application", "x-format"));
final String response = request.put(Entity.entity(entity, "multipart/form-data"), String.class);
if (!response.startsWith("SUCCESS:")) {
fail("Response is '" + response + "'");
}
}
@Path("/ConsumesFormDataParamResource")
public static class ConsumesFormDataParamResource {
@PUT
@Consumes("multipart/form-data")
@Produces("text/plain")
public Response get(
@FormDataParam("foo") final String foo,
@FormDataParam("baz") final String baz,
@FormDataParam("bean") final MultiPartBean bean,
@FormDataParam("unknown1") final String unknown1,
@FormDataParam("unknown2") @DefaultValue("UNKNOWN") final String unknown2,
final FormDataMultiPart fdmp) {
if (!"bar".equals(foo)) {
return Response.ok("FAILED: Value of 'foo' is '" + foo + "' instead of 'bar'").build();
} else if (!"bop".equals(baz)) {
return Response.ok("FAILED: Value of 'baz' is '" + baz + "' instead of 'bop'").build();
} else if (bean == null) {
return Response.ok("FAILED: Value of 'bean' is NULL").build();
} else if (!(bean.getName().equals("myname") && bean.getValue().equals("myvalue"))) {
return Response
.ok("FAILED: Value of 'bean.myName' and 'bean.MyValue' are not 'myname' and 'myvalue'")
.build();
} else if (unknown1 != null) {
return Response.ok("FAILED: Value of 'unknown1' is '" + unknown1 + "' instead of NULL").build();
} else if (!"UNKNOWN".equals(unknown2)) {
return Response.ok("FAILED: Value of 'unknown2' is '" + unknown2 + "' instead of 'UNKNOWN'").build();
} else if (fdmp == null) {
return Response.ok("FAILED: Value of fdmp is NULL").build();
} else if (fdmp.getFields().size() != 3) {
return Response
.ok("FAILED: Value of fdmp.getFields().size() is " + fdmp.getFields().size() + " instead of 3")
.build();
}
return Response.ok("SUCCESS: All tests passed").build();
}
}
@Test
public void testConsumesFormDataParamResource() {
final Invocation.Builder request = target().path("ConsumesFormDataParamResource").request("text/plain");
final MultiPartBean bean = new MultiPartBean("myname", "myvalue");
final FormDataMultiPart entity = new FormDataMultiPart()
.field("foo", "bar")
.field("baz", "bop")
.field("bean", bean, new MediaType("x-application", "x-format"));
final String response = request.put(Entity.entity(entity, "multipart/form-data"), String.class);
if (!response.startsWith("SUCCESS:")) {
fail("Response is '" + response + "'");
}
}
@Path("/FormDataTypesResource")
public static class FormDataTypesResource {
@PUT
@Consumes("multipart/form-data")
@Produces("text/plain")
public String get(
@FormDataParam("foo") final FormDataContentDisposition fooDisp,
@FormDataParam("foo") final FormDataBodyPart fooPart,
@FormDataParam("baz") final FormDataContentDisposition bazDisp,
@FormDataParam("baz") final FormDataBodyPart bazPart) throws IOException {
assertNotNull(fooDisp);
assertNotNull(fooPart);
assertNotNull(bazDisp);
assertNotNull(bazPart);
assertEquals("foo", fooDisp.getName());
assertEquals("foo", fooPart.getName());
assertEquals("bar", fooPart.getValue());
assertEquals("baz", bazDisp.getName());
assertEquals("baz", bazPart.getName());
assertEquals("bop", bazPart.getValue());
return "OK";
}
}
@Test
public void testFormDataTypesResource() {
final Invocation.Builder request = target().path("FormDataTypesResource").request("text/plain");
final FormDataMultiPart entity = new FormDataMultiPart()
.field("foo", "bar")
.field("baz", "bop");
final String response = request.put(Entity.entity(entity, "multipart/form-data"), String.class);
assertEquals("OK", response);
}
@Path("/FormDataListTypesResource")
public static class FormDataListTypesResource {
@PUT
@Consumes("multipart/form-data")
@Produces("text/plain")
public String get(
@FormDataParam("foo") final List<FormDataContentDisposition> fooDisp,
@FormDataParam("foo") final List<FormDataBodyPart> fooPart,
@FormDataParam("baz") final List<FormDataContentDisposition> bazDisp,
@FormDataParam("baz") final List<FormDataBodyPart> bazPart) {
assertNotNull(fooDisp);
assertNotNull(fooPart);
assertNotNull(bazDisp);
assertNotNull(bazPart);
assertEquals(2, fooDisp.size());
assertEquals(2, fooPart.size());
assertEquals(2, bazDisp.size());
assertEquals(2, bazPart.size());
return "OK";
}
}
@Test
public void testFormDataListTypesResource() {
final Invocation.Builder request = target().path("FormDataListTypesResource").request("text/plain");
final FormDataMultiPart entity = new FormDataMultiPart()
.field("foo", "bar")
.field("foo", "bar2")
.field("baz", "bop")
.field("baz", "bop2");
final String response = request.put(Entity.entity(entity, "multipart/form-data"), String.class);
assertEquals("OK", response);
}
@Path("/FormDataCollectionTypesResource")
public static class FormDataCollectionTypesResource {
@PUT
@Consumes("multipart/form-data")
@Produces("text/plain")
public String get(
@FormDataParam("foo") final Collection<FormDataContentDisposition> fooDisp,
@FormDataParam("foo") final Collection<FormDataBodyPart> fooPart,
@FormDataParam("baz") final Collection<FormDataContentDisposition> bazDisp,
@FormDataParam("baz") final Collection<FormDataBodyPart> bazPart) {
assertNotNull(fooDisp);
assertNotNull(fooPart);
assertNotNull(bazDisp);
assertNotNull(bazPart);
assertEquals(2, fooDisp.size());
assertEquals(2, fooPart.size());
assertEquals(2, bazDisp.size());
assertEquals(2, bazPart.size());
return "OK";
}
}
@Test
public void testFormDataCollectionTypesResource() {
final Invocation.Builder request = target().path("FormDataCollectionTypesResource").request("text/plain");
final FormDataMultiPart entity = new FormDataMultiPart()
.field("foo", "bar")
.field("foo", "bar2")
.field("baz", "bop")
.field("baz", "bop2");
final String response = request.put(Entity.entity(entity, "multipart/form-data"), String.class);
assertEquals("OK", response);
}
@Path("/PrimitivesFormDataParamResource")
public static class PrimitivesFormDataParamResource {
@PUT
@Consumes("multipart/form-data")
@Produces("text/plain")
public String get(
@FormDataParam("bP") final boolean bP,
@FormDataParam("bT") final Boolean bT,
@FormDataParam("bP_absent") final boolean bP_absent,
@FormDataParam("bT_absent") final Boolean bT_absent,
@DefaultValue("true") @FormDataParam("bP_absent_default") final boolean bP_absent_default,
@DefaultValue("true") @FormDataParam("bT_absent_default") final Boolean bT_absent_default,
@DefaultValue("true") @FormDataParam("bP_default") final boolean bP_default,
@DefaultValue("true") @FormDataParam("bT_default") final Boolean bT_default
) {
assertTrue(bP);
assertTrue(bT);
assertFalse(bP_absent);
assertNull(bT_absent);
assertTrue(bP_absent_default);
assertTrue(bT_absent_default);
assertFalse(bP_default);
assertFalse(bT_default);
return "OK";
}
}
@Test
public void testPrimitivesFormDataParamResource() {
final Invocation.Builder request = target().path("PrimitivesFormDataParamResource").request("text/plain");
final FormDataMultiPart entity = new FormDataMultiPart()
.field("bP", "true")
.field("bT", "true")
.field("bP_default", "false")
.field("bT_default", "false");
final String response = request.put(Entity.entity(entity, "multipart/form-data"), String.class);
assertEquals("OK", response);
}
@Path("/DefaultFormDataParamResource")
public static class DefaultFormDataParamResource {
@PUT
@Consumes("multipart/form-data")
@Produces("text/plain")
public String get(
@FormDataParam("bean") final MultiPartBean bean,
@FormDataParam("bean_absent") final MultiPartBean bean_absent,
@DefaultValue("myname=myvalue") @FormDataParam("bean_default") final MultiPartBean bean_default) {
assertNotNull(bean);
assertNull(bean_absent);
assertNull(bean_default);
assertEquals("myname", bean.getName());
assertEquals("myvalue", bean.getValue());
return "OK";
}
}
@Test
public void testDefaultFormDataParamResource() {
final Invocation.Builder request = target().path("DefaultFormDataParamResource").request("text/plain");
final MultiPartBean bean = new MultiPartBean("myname", "myvalue");
final FormDataMultiPart entity = new FormDataMultiPart()
.field("bean", bean, new MediaType("x-application", "x-format"));
final String response = request.put(Entity.entity(entity, "multipart/form-data"), String.class);
assertEquals("OK", response);
}
@Path("/NonContentTypeForPartResource")
public static class NonContentTypeForPartResource {
@PUT
@Consumes("multipart/form-data")
@Produces("text/plain")
public String put(@FormDataParam("submit") final FormDataBodyPart bean) throws IOException {
assertNotNull(bean);
assertNull(bean.getHeaders().getFirst("Content-Type"));
assertEquals("upload", bean.getValue());
return "OK";
}
}
@Test
public void testNonContentTypeForPartResource() {
final Invocation.Builder request = target().path("NonContentTypeForPartResource").request("text/plain");
final String entity =
"-----------------------------33219615019106944971719437488\n"
+ "Content-Disposition: form-data; name=\"submit\"\n\n"
+ "upload\n"
+ "-----------------------------33219615019106944971719437488--";
final String response = request.put(Entity.entity(entity, "multipart/form-data;"
+ "boundary=\"---------------------------33219615019106944971719437488\""), String.class);
assertEquals("OK", response);
}
@Path("/MediaTypeWithBoundaryResource")
public static class MediaTypeWithBoundaryResource {
@PUT
@Consumes("multipart/form-data")
@Produces("text/plain")
public String get(
@Context final HttpHeaders h,
@FormDataParam("submit") final String s) {
final String b = h.getMediaType().getParameters().get("boundary");
assertEquals("XXXX_YYYY", b);
return s;
}
}
@Test
public void testMediaTypeWithBoundaryResource() {
final Map<String, String> parameters = new HashMap<>();
parameters.put("boundary", "XXXX_YYYY");
final MediaType mediaType = new MediaType(
MediaType.MULTIPART_FORM_DATA_TYPE.getType(),
MediaType.MULTIPART_FORM_DATA_TYPE.getSubtype(), parameters);
final FormDataMultiPart entity = new FormDataMultiPart().field("submit", "OK");
final Invocation.Builder request = target().path("MediaTypeWithBoundaryResource").request("text/plain");
final String response = request.put(Entity.entity(entity, mediaType), String.class);
assertEquals("OK", response);
}
@Test
public void testMediaTypeWithQuotedBoundaryResource() throws Exception {
final URL url = new URL(getBaseUri().toString() + "MediaTypeWithBoundaryResource");
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("PUT");
connection.setRequestProperty("Accept", "text/plain");
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=\"XXXX_YYYY\"");
connection.setDoOutput(true);
connection.connect();
final OutputStream outputStream = connection.getOutputStream();
outputStream.write("--XXXX_YYYY".getBytes());
outputStream.write('\n');
outputStream.write("Content-Type: text/plain".getBytes());
outputStream.write('\n');
outputStream.write("Content-Disposition: form-data; name=\"submit\"".getBytes());
outputStream.write('\n');
outputStream.write('\n');
outputStream.write("OK".getBytes());
outputStream.write('\n');
outputStream.write("--XXXX_YYYY--".getBytes());
outputStream.write('\n');
outputStream.flush();
assertEquals("OK", connection.getResponseMessage());
}
@Path("/FileResource")
@Consumes("multipart/form-data")
@Produces("text/plain")
public static class FileResource {
@POST
@Path("InjectedFileNotCopied")
public String injectedFileNotCopied(@FormDataParam("file") final File file) {
final String path = file.getAbsolutePath();
//noinspection ResultOfMethodCallIgnored
file.delete();
return path;
}
@POST
@Path("ExceptionInMethod")
public String exceptionInMethod(@FormDataParam("file") final File file) {
throw new WebApplicationException(Response.serverError().entity(file.getAbsolutePath()).build());
}
@POST
@Path("SuccessfulMethod")
public String successfulMethod(@FormDataParam("file") final File file) {
return file.getAbsolutePath();
}
@POST
@Path("FileSize")
public long fileSize(@FormDataParam("file") final File file) {
return file.length();
}
}
/**
* JERSEY-2663 reproducer. Make sure that temporary file created by MIMEPull is not copied into new temporary file created
* by Jersey.
*/
@Test
public void testInjectedFileNotCopied(@Mocked final BodyPartEntity entity) throws Exception {
final FormDataMultiPart multipart = new FormDataMultiPart();
final FormDataBodyPart bodypart = new FormDataBodyPart(FormDataContentDisposition.name("file").fileName("file").build(),
"CONTENT");
multipart.bodyPart(bodypart);
final Response response = target().path("FileResource").path("InjectedFileNotCopied")
.request()
.post(Entity.entity(multipart, MediaType.MULTIPART_FORM_DATA));
// Make sure that the Mimepull temp file has been moved to specific file.
new Verifications() {{
entity.moveTo(withInstanceOf(File.class));
times = 1;
}};
// Make sure that the temp file has been removed.
final String pathname = response.readEntity(String.class);
// Wait a second to make sure the file doesn't exist.
Thread.sleep(1000);
assertThat("Temporary file, " + pathname + ", on the server has not been removed",
new File(pathname).exists(), is(false));
}
/**
* JERSEY-2846 reproducer. Make sure that temporary file created by MIMEPull deleted after a successful request.
*/
@Test
public void tempFileDeletedAfterSuccessfulProcessing() throws Exception {
final FormDataMultiPart multipart = new FormDataMultiPart();
final FormDataBodyPart bodypart = new FormDataBodyPart(FormDataContentDisposition.name("file").fileName("file").build(),
"CONTENT");
multipart.bodyPart(bodypart);
final Response response = target().path("FileResource").path("SuccessfulMethod")
.request()
.post(Entity.entity(multipart, MediaType.MULTIPART_FORM_DATA));
// Make sure that the temp file has been removed.
final String pathname = response.readEntity(String.class);
// Wait a second to make sure the file doesn't exist.
Thread.sleep(1000);
assertThat("Temporary file, " + pathname + ", on the server has not been removed",
new File(pathname).exists(), is(false));
}
/**
* JERSEY-2846 reproducer. Make sure that temporary file created by MIMEPull deleted after an unsuccessful request.
*/
@Test
public void tempFileDeletedAfterExceptionInMethod() throws Exception {
final FormDataMultiPart multipart = new FormDataMultiPart();
final FormDataBodyPart bodypart = new FormDataBodyPart(FormDataContentDisposition.name("file").fileName("file").build(),
"CONTENT");
multipart.bodyPart(bodypart);
final Response response = target().path("FileResource").path("ExceptionInMethod")
.request()
.post(Entity.entity(multipart, MediaType.MULTIPART_FORM_DATA));
// Make sure that the temp file has been removed.
final String pathname = response.readEntity(String.class);
// Wait a second to make sure the file doesn't exist.
Thread.sleep(1000);
assertThat("Temporary file, " + pathname + ", on the server has not been removed",
new File(pathname).exists(), is(false));
}
/**
* JERSEY-2862 reproducer. Make sure that mimepull is able to move it's temporary file to the one created by Jersey.
* Reproducible only on Windows. Entity size has to be bigger than 8192 to make sure mimepull creates temporary file.
*/
@Test
public void testFileSize() throws Exception {
final FormDataMultiPart multipart = new FormDataMultiPart();
final byte[] content = new byte[2 * 8192];
final FormDataBodyPart bodypart = new FormDataBodyPart(FormDataContentDisposition.name("file").fileName("file").build(),
content, MediaType.TEXT_PLAIN_TYPE);
multipart.bodyPart(bodypart);
final Response response = target().path("FileResource").path("FileSize")
.request()
.post(Entity.entity(multipart, MediaType.MULTIPART_FORM_DATA));
assertThat("Temporary file has wrong size.", response.readEntity(int.class), is(content.length));
}
@Path("/InputStreamResource")
public static class InputStreamResource {
@PUT
@Consumes("multipart/form-data")
@Produces("text/plain")
public String put(@FormDataParam("submit") final InputStream stream) {
return "OK";
}
}
/**
* Mocked JERSEY-2794 reproducer. Real test is under integration tests.
*/
@Test
public void mimeTempFileRemovedAfterAbortedUpload(@Mocked final MIMEMessage message) throws Exception {
new Expectations() {{
message.getAttachments();
result = new MIMEParsingException();
}};
final URL url = new URL(getBaseUri().toString() + "MediaTypeWithBoundaryResource");
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("PUT");
connection.setRequestProperty("Accept", "text/plain");
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=XXXX_YYYY");
connection.setDoOutput(true);
connection.connect();
final OutputStream outputStream = connection.getOutputStream();
outputStream.write("--XXXX_YYYY".getBytes());
outputStream.write('\n');
outputStream.write("Content-Type: text/plain".getBytes());
outputStream.write('\n');
outputStream.write("Content-Disposition: form-data; name=\"big-part\"".getBytes());
outputStream.write('\n');
outputStream.write('\n');
// Send big chunk of data.
for (int i = 0; i < 16 * 4096; i++) {
outputStream.write('E');
if (i % 1024 == 0) {
outputStream.flush();
}
}
// Do NOT send end of the MultiPart message to simulate the issue.
// Get Response ...
final int response = connection.getResponseCode();
// ... Disconnect.
connection.disconnect();
assertThat("Bad Request expected", response, is(400));
// Make sure that the Mimepull message and it's parts have been closed and temporary files deleted.
new Verifications() {{
message.close();
times = 1;
}};
}
private void checkEntity(final String expected, final BodyPartEntity entity) throws IOException {
// Convert the raw bytes into a String
final InputStreamReader sr = new InputStreamReader(entity.getInputStream());
final StringWriter sw = new StringWriter();
while (true) {
final int ch = sr.read();
if (ch < 0) {
break;
}
sw.append((char) ch);
}
// Perform the comparison
assertEquals(expected, sw.toString());
}
private void checkMediaType(final MediaType expected, final MediaType actual) {
assertEquals("Expected MediaType=" + expected, expected.getType(), actual.getType());
assertEquals("Expected MediaType=" + expected, expected.getSubtype(), actual.getSubtype());
}
}