package org.ovirt.engine.core.utils.servlet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import javax.imageio.ImageIO;
import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.junit.Before;
import org.junit.Test;
public class ServletUtilsTest {
private String canReadFileName;
@Before
public void setup() throws IOException, URISyntaxException {
canReadFileName = this.getClass().getResource("small_file.txt").toURI().toASCIIString().replaceAll("file:", "");
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#canReadFile(java.io.File)}.
*/
@Test
public void testCanReadFile() {
// Make sure the user is not root
// This tests relies on EXISTING files which only root can access
String userName = System.getProperty("user.name");
assumeTrue(!"root".equals(userName));
//Does not exist.
File file = new File("/doesnotexist/iamprettysure");
assertFalse("We should not be able to read this file.", ServletUtils.canReadFile(file));
//Exists, but should not be readable.
file = new File("/etc/securetty");
assertFalse("We should not be able to read this file.", ServletUtils.canReadFile(file));
//Exists and we can read.
file = new File(canReadFileName);
assertTrue("We should be able to read this file.", ServletUtils.canReadFile(file));
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#writeFileToStream(java.io.OutputStream, java.io.File)}.
*/
@Test
public void testWriteFileToStream() throws IOException {
File file = new File(canReadFileName);
long hostSize = file.length();
assertTrue("We should be able to read this file.", ServletUtils.canReadFile(file));
ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
ServletUtils.writeFileToStream(out, file);
assertEquals("The bytes in the buffer have to match the length of the file", (int)hostSize, out.size());
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#writeFileToStream(java.io.OutputStream, java.io.File)}.
*/
@Test
public void testWriteFileToStream_0SizeFile() throws Exception {
File file = new File(this.getClass().getResource("zerosize").toURI());
long zeroSize = file.length();
assertTrue("We should be able to read this file.", ServletUtils.canReadFile(file));
assertEquals("The file size should be 0", 0L, zeroSize);
ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
ServletUtils.writeFileToStream(out, file);
assertEquals("The bytes in the buffer have to match the length of the file", (int)zeroSize, out.size());
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#writeFileToStream(java.io.OutputStream, java.io.File)}.
*/
@Test(expected = IOException.class)
public void testWriteFileToStream_IOException() throws IOException {
File file = new File("/doesnotexist/iamprettysure");
//Make a large buffer.
ByteArrayOutputStream out = new ByteArrayOutputStream();
ServletUtils.writeFileToStream(out, file);
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#getFileSize(java.io.File)}.
*/
@Test
public void testGetFileSize() {
File file = new File(canReadFileName);
long hostsSize = file.length();
assertTrue("We should be able to read this file.", ServletUtils.canReadFile(file));
assertEquals("Values should match", hostsSize, ServletUtils.getFileSize(file));
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#isSane(java.lang.String)}.
*/
@Test
public void testIsSane() {
assertTrue("/etc should be sane", ServletUtils.isSane("/etc"));
StringBuilder builder = new StringBuilder();
for(int i = 0; i < 100; i++) {
builder.append("/abcdefghijkl");
}
assertFalse("longPath is not sane", ServletUtils.isSane(builder.toString()));
assertFalse("path with .. is not sane", ServletUtils.isSane("/something/../etc/password"));
assertFalse("path with // is not sane", ServletUtils.isSane("/something//etc/password"));
assertFalse("path with ./ is not sane", ServletUtils.isSane("/something/./etc/password"));
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#makeFileFromSanePath(java.lang.String, java.io.File)}.
*/
@Test
public void testGetFileFromString_NullPath() {
File file = new File(canReadFileName);
File testFile = ServletUtils.makeFileFromSanePath(null, file);
assertEquals("new file should be same as old file", file, testFile);
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#makeFileFromSanePath(java.lang.String, java.io.File)}.
*/
@Test
public void testGetFileFromString_Happy() throws URISyntaxException {
String path = this.getClass().getResource(".").toURI().toASCIIString().replaceAll("file:", "");
File file = new File(path);
File testFile = ServletUtils.makeFileFromSanePath("small_file.txt", file);
assertEquals("new file should be same as old file", new File(canReadFileName), testFile);
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#makeFileFromSanePath(java.lang.String, java.io.File)}.
*/
@Test
public void testGetFileFrom_InsanePath() {
File file = new File("/etc");
File testFile = ServletUtils.makeFileFromSanePath("/../hosts", file);
assertNull("testfile should be null", testFile);
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#sendFile(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.io.File, java.lang.String)}.
*/
@Test
public void testSendFile_MissingFile() throws IOException {
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
ServletOutputStream responseOut = mock(ServletOutputStream.class);
when(mockResponse.getOutputStream()).thenReturn(responseOut);
File file = new File("/etc/doesntexist");
ServletUtils.sendFile(mockRequest, mockResponse, file, null);
verify(mockResponse).sendError(HttpServletResponse.SC_NOT_FOUND);
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#sendFile(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.io.File, java.lang.String)}.
*/
@Test
public void testSendFile_PNG() throws IOException {
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
ServletOutputStream responseOut = mock(ServletOutputStream.class);
when(mockResponse.getOutputStream()).thenReturn(responseOut);
File file = createTempPng();
ServletUtils.sendFile(mockRequest, mockResponse, file, "application/xml");
//Check that the mime type was set to the one passed in, instead of the one associated with the file
verify(mockResponse).setContentType("application/xml");
//Check the file length is set right.
verify(mockResponse).setContentLength((int) file.length());
//Make sure the stream is written to.
verify(responseOut).write(any(), eq(0), anyInt());
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#sendFile(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.io.File, java.lang.String)}.
*/
@Test
public void testSendFile_PNGNoMime() throws IOException {
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
ServletOutputStream responseOut = mock(ServletOutputStream.class);
when(mockResponse.getOutputStream()).thenReturn(responseOut);
File file = createTempPng();
ServletUtils.sendFile(mockRequest, mockResponse, file, null);
//Check that the mime type was set to the one passed in, instead of the one associated with the file
verify(mockResponse).setContentType("image/png");
//Check the file length is set right.
verify(mockResponse).setContentLength((int) file.length());
//Make sure the stream is written to.
verify(responseOut).write(any(), eq(0), anyInt());
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#sendFile(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.io.File, java.lang.String, boolean)}.
*/
@Test
public void testSendFile_PNGNoMime_Cache() throws IOException {
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
ServletOutputStream responseOut = mock(ServletOutputStream.class);
when(mockResponse.getOutputStream()).thenReturn(responseOut);
File file = createTempPng();
ServletUtils.sendFile(mockRequest, mockResponse, file, null, true);
//Check that we have eTag
verify(mockResponse).setHeader("ETag", ServletUtils.getETag(file));
//Check that the mime type was set to the one passed in, instead of the one associated with the file
verify(mockResponse).setContentType("image/png");
//Check the file length is set right.
verify(mockResponse).setContentLength((int) file.length());
//Make sure the stream is written to.
verify(responseOut).write(any(), eq(0), anyInt());
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#sendFile(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.io.File, java.lang.String, boolean)}.
*/
@Test
public void testSendFile_PNGNoMime_NoCache() throws IOException {
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
ServletOutputStream responseOut = mock(ServletOutputStream.class);
when(mockResponse.getOutputStream()).thenReturn(responseOut);
File file = createTempPng();
ServletUtils.sendFile(mockRequest, mockResponse, file, null, false);
//Check that we have eTag
verify(mockResponse, never()).setHeader(eq("ETag"), anyString());
//Check that the mime type was set to the one passed in, instead of the one associated with the file
verify(mockResponse).setContentType("image/png");
//Check the file length is set right.
verify(mockResponse).setContentLength((int) file.length());
//Make sure the stream is written to.
verify(responseOut).write(any(), eq(0), anyInt());
}
private File createTempPng() throws IOException {
File file = File.createTempFile("favicon", ".png");
file.deleteOnExit();
BufferedImage img = new BufferedImage(256, 256,
BufferedImage.TYPE_INT_RGB);
if (!ImageIO.write(img, "PNG", file)) {
fail("Unable to write temporary image file");
}
return file;
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#sendFile(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.io.File, java.lang.String)}.
*/
@Test
public void test_ETag_Format() throws IOException {
File mockFile = mock(File.class);
when(mockFile.length()).thenReturn(1234L);
when(mockFile.lastModified()).thenReturn(8271L);
assertEquals("ETag does not match", "W/\"1234-8271\"", ServletUtils.getETag(mockFile));
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#sendFile(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.io.File, java.lang.String)}.
*/
@Test
public void testSendFile_ETag_None() throws IOException {
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
ServletOutputStream responseOut = mock(ServletOutputStream.class);
when(mockResponse.getOutputStream()).thenReturn(responseOut);
File file = createTempPng();
ServletUtils.sendFile(mockRequest, mockResponse, file, null);
verify(mockResponse).setHeader("ETag", ServletUtils.getETag(file));
verify(responseOut).write(any(), eq(0), anyInt());
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#sendFile(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.io.File, java.lang.String)}.
*/
@Test
public void testSendFile_ETag_Same() throws IOException {
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
ServletOutputStream responseOut = mock(ServletOutputStream.class);
File file = createTempPng();
when(mockResponse.getOutputStream()).thenReturn(responseOut);
when(mockRequest.getHeader("If-None-Match")).thenReturn(ServletUtils.getETag(file));
ServletUtils.sendFile(mockRequest, mockResponse, file, null);
verify(mockResponse).setHeader("ETag", ServletUtils.getETag(file));
verify(mockResponse).setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#sendFile(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.io.File, java.lang.String)}.
*/
@Test
public void testSendFile_ETag_All() throws IOException {
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
ServletOutputStream responseOut = mock(ServletOutputStream.class);
File file = createTempPng();
when(mockResponse.getOutputStream()).thenReturn(responseOut);
when(mockRequest.getHeader("If-None-Match")).thenReturn("*");
ServletUtils.sendFile(mockRequest, mockResponse, file, null);
verify(mockResponse).setHeader("ETag", ServletUtils.getETag(file));
verify(mockResponse).setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
}
/**
* Test method for {@link org.ovirt.engine.core.utils.servlet.ServletUtils#sendFile(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.io.File, java.lang.String)}.
*/
@Test
public void testSendFile_ETag_Different() throws IOException {
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
ServletOutputStream responseOut = mock(ServletOutputStream.class);
File file = createTempPng();
when(mockResponse.getOutputStream()).thenReturn(responseOut);
when(mockRequest.getHeader("If-None-Match")).thenReturn("xxxx");
ServletUtils.sendFile(mockRequest, mockResponse, file, null);
verify(mockResponse).setHeader("ETag", ServletUtils.getETag(file));
verify(responseOut).write(any(), eq(0), anyInt());
}
@Test
public void testGetAsAbsoluteContext() {
String result = ServletUtils.getAsAbsoluteContext("/ovirt-engine/testpath", "..");
assertEquals("The result should be '/ovirt-engine/'", "/ovirt-engine/", result);
result = ServletUtils.getAsAbsoluteContext("/ovirt-engine/testpath", "/something");
assertEquals("The result should be '/something'", "/something", result);
result = ServletUtils.getAsAbsoluteContext("/ovirt-engine/testpath", "../somethingelse");
assertEquals("The result should be '/ovirt-engine/somethingelse'", "/ovirt-engine/somethingelse", result);
result = ServletUtils.getAsAbsoluteContext("/ovirt-engine/testpath", ".");
assertEquals("The result should be '/ovirt-engine/testpath/'", "/ovirt-engine/testpath/", result);
}
@Test
public void testGetBaseContextPath() {
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
ServletContext mockServletContext = mock(ServletContext.class);
HttpSession mockSession = mock(HttpSession.class);
when(mockRequest.getSession()).thenReturn(mockSession);
when(mockSession.getServletContext()).thenReturn(mockServletContext);
when(mockRequest.getContextPath()).thenReturn("/ovirt-engine/test");
when(mockServletContext.getInitParameter(ServletUtils.CONTEXT_TO_ROOT_MODIFIER)).thenReturn("..");
String result = ServletUtils.getBaseContextPath(mockRequest);
assertEquals("Result should be '/ovirt-engine/'", "/ovirt-engine/", result);
}
@Test
public void testGetBaseContextPath2() {
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
ServletContext mockServletContext = mock(ServletContext.class);
HttpSession mockSession = mock(HttpSession.class);
when(mockRequest.getSession()).thenReturn(mockSession);
when(mockSession.getServletContext()).thenReturn(mockServletContext);
when(mockRequest.getContextPath()).thenReturn("/ovirt-engine/test");
when(mockServletContext.getInitParameter(ServletUtils.CONTEXT_TO_ROOT_MODIFIER)).thenReturn("../..");
String result = ServletUtils.getBaseContextPath(mockRequest);
assertEquals("Result should be '/'", "/", result);
}
}