Jersey2794ITCase.java
/*
* Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.jersey.tests.integration.jersey2794;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.atomic.AtomicInteger;
import javax.ws.rs.core.Application;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
import org.glassfish.jersey.test.spi.TestContainerException;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* JERSEY-2794 reproducer.
*
* @author Michal Gajdos
*/
public class Jersey2794ITCase extends JerseyTest {
@Override
protected Application configure() {
return new Application();
}
@Override
protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
return new ExternalTestContainerFactory();
}
@Test
public void mimeTempFileRemoved() throws Exception {
final String tempDir = System.getProperty("java.io.tmpdir");
// Get number of matching MIME*tmp files (the number should be the same at the end of the test).
final int expectedTempFiles = matchingTempFiles(tempDir);
final URL url = new URL(getBaseUri().toString());
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 ...
assertThat("Bad Request expected", connection.getResponseCode(), is(400));
// Make sure that the Mimepull message and it's parts have been closed and temporary files deleted.
assertThat("Temporary mimepull files were not deleted", matchingTempFiles(tempDir), is(expectedTempFiles));
// ... Disconnect.
connection.disconnect();
}
private int matchingTempFiles(final String tempDir) throws IOException {
AtomicInteger count = new AtomicInteger(0);
Files.walkFileTree(Paths.get(tempDir), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.getFileName().startsWith("MIME") && file.getFileName().endsWith("tmp")) {
count.incrementAndGet();
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
});
return count.get();
}
}