Merge branch `jetty-9.2.x` into `jetty-9.3.x`
Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com> # Conflicts: # jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/HttpSpiContextHandler.java # jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpServerProvider.java # jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyDeployWar.java # jetty-security/src/test/java/org/eclipse/jetty/security/AliasedConstraintTest.java # jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java # jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java # jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java # jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java # jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java
This commit is contained in:
commit
554fc39f06
|
@ -25,7 +25,6 @@ import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
import java.nio.channels.FileChannel.MapMode;
|
import java.nio.channels.FileChannel.MapMode;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
|
|
||||||
import javax.servlet.AsyncContext;
|
import javax.servlet.AsyncContext;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
@ -116,7 +115,8 @@ public class FastFileServer
|
||||||
}
|
}
|
||||||
String listing = Resource.newResource(file).getListHTML(
|
String listing = Resource.newResource(file).getListHTML(
|
||||||
request.getRequestURI(),
|
request.getRequestURI(),
|
||||||
request.getPathInfo().lastIndexOf("/") > 0);
|
request.getPathInfo().lastIndexOf("/") > 0,
|
||||||
|
request.getQueryString());
|
||||||
response.setContentType("text/html; charset=utf-8");
|
response.setContentType("text/html; charset=utf-8");
|
||||||
response.getWriter().println(listing);
|
response.getWriter().println(listing);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -23,7 +23,6 @@ import java.io.OutputStream;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
|
||||||
import javax.servlet.AsyncContext;
|
import javax.servlet.AsyncContext;
|
||||||
import javax.servlet.RequestDispatcher;
|
import javax.servlet.RequestDispatcher;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
@ -182,7 +181,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory
|
||||||
Context scontext = ContextHandler.getCurrentContext();
|
Context scontext = ContextHandler.getCurrentContext();
|
||||||
_context = (scontext==null?null:scontext.getContextHandler());
|
_context = (scontext==null?null:scontext.getContextHandler());
|
||||||
_mimeTypes = _context==null?new MimeTypes():_context.getMimeTypes();
|
_mimeTypes = _context==null?new MimeTypes():_context.getMimeTypes();
|
||||||
|
|
||||||
super.doStart();
|
super.doStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,7 +307,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory
|
||||||
|
|
||||||
if (path==null || !path.startsWith("/"))
|
if (path==null || !path.startsWith("/"))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Resource base = _baseResource;
|
Resource base = _baseResource;
|
||||||
|
@ -504,10 +503,10 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory
|
||||||
baseRequest.getResponse().getHttpFields().put(HttpHeader.ETAG,etag);
|
baseRequest.getResponse().getHttpFields().put(HttpHeader.ETAG,etag);
|
||||||
if (last_modified>0)
|
if (last_modified>0)
|
||||||
response.setDateHeader(HttpHeader.LAST_MODIFIED.asString(),last_modified);
|
response.setDateHeader(HttpHeader.LAST_MODIFIED.asString(),last_modified);
|
||||||
|
|
||||||
if(skipContentBody)
|
if(skipContentBody)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Send the content
|
// Send the content
|
||||||
OutputStream out =null;
|
OutputStream out =null;
|
||||||
try {out = response.getOutputStream();}
|
try {out = response.getOutputStream();}
|
||||||
|
@ -546,7 +545,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory
|
||||||
};
|
};
|
||||||
|
|
||||||
// Can we use a memory mapped file?
|
// Can we use a memory mapped file?
|
||||||
if (_minMemoryMappedContentLength>=0 &&
|
if (_minMemoryMappedContentLength>=0 &&
|
||||||
resource.length()>_minMemoryMappedContentLength &&
|
resource.length()>_minMemoryMappedContentLength &&
|
||||||
resource.length()<Integer.MAX_VALUE &&
|
resource.length()<Integer.MAX_VALUE &&
|
||||||
resource instanceof PathResource)
|
resource instanceof PathResource)
|
||||||
|
@ -592,7 +591,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory
|
||||||
{
|
{
|
||||||
if (_directory)
|
if (_directory)
|
||||||
{
|
{
|
||||||
String listing = resource.getListHTML(request.getRequestURI(),request.getPathInfo().lastIndexOf("/") > 0);
|
String listing = resource.getListHTML(request.getRequestURI(),request.getPathInfo().lastIndexOf("/") > 0, request.getQueryString());
|
||||||
response.setContentType("text/html;charset=utf-8");
|
response.setContentType("text/html;charset=utf-8");
|
||||||
response.getWriter().println(listing);
|
response.getWriter().println(listing);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,6 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.servlet;
|
package org.eclipse.jetty.servlet;
|
||||||
|
|
||||||
import static org.eclipse.jetty.http.GzipHttpContent.ETAG_GZIP_QUOTE;
|
|
||||||
import static org.eclipse.jetty.http.GzipHttpContent.removeGzipFromETag;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -33,7 +30,6 @@ import java.util.ArrayList;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import javax.servlet.AsyncContext;
|
import javax.servlet.AsyncContext;
|
||||||
import javax.servlet.RequestDispatcher;
|
import javax.servlet.RequestDispatcher;
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
|
@ -72,6 +68,9 @@ import org.eclipse.jetty.util.resource.Resource;
|
||||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||||
|
|
||||||
|
import static org.eclipse.jetty.http.GzipHttpContent.ETAG_GZIP_QUOTE;
|
||||||
|
import static org.eclipse.jetty.http.GzipHttpContent.removeGzipFromETag;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default servlet.
|
* The default servlet.
|
||||||
|
@ -869,7 +868,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
||||||
else if (_contextHandler.getBaseResource() instanceof ResourceCollection)
|
else if (_contextHandler.getBaseResource() instanceof ResourceCollection)
|
||||||
resource=_contextHandler.getBaseResource().addPath(pathInContext);
|
resource=_contextHandler.getBaseResource().addPath(pathInContext);
|
||||||
|
|
||||||
String dir = resource.getListHTML(base,pathInContext.length()>1);
|
String dir = resource.getListHTML(base,pathInContext.length()>1, request.getQueryString());
|
||||||
if (dir==null)
|
if (dir==null)
|
||||||
{
|
{
|
||||||
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||||
|
|
|
@ -18,13 +18,6 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.servlet;
|
package org.eclipse.jetty.servlet;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.anyOf;
|
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
|
||||||
import static org.hamcrest.CoreMatchers.not;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -34,7 +27,6 @@ import java.nio.file.Files;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.servlet.DispatcherType;
|
import javax.servlet.DispatcherType;
|
||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
|
@ -65,6 +57,13 @@ import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.anyOf;
|
||||||
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.hamcrest.CoreMatchers.not;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class DefaultServletTest
|
public class DefaultServletTest
|
||||||
{
|
{
|
||||||
@Rule
|
@Rule
|
||||||
|
@ -144,9 +143,14 @@ public class DefaultServletTest
|
||||||
/* create some content in the docroot */
|
/* create some content in the docroot */
|
||||||
File resBase = testdir.getPathFile("docroot").toFile();
|
File resBase = testdir.getPathFile("docroot").toFile();
|
||||||
FS.ensureDirExists(resBase);
|
FS.ensureDirExists(resBase);
|
||||||
assertTrue(new File(resBase, "one").mkdir());
|
File one = new File(resBase, "one");
|
||||||
|
assertTrue(one.mkdir());
|
||||||
assertTrue(new File(resBase, "two").mkdir());
|
assertTrue(new File(resBase, "two").mkdir());
|
||||||
assertTrue(new File(resBase, "three").mkdir());
|
assertTrue(new File(resBase, "three").mkdir());
|
||||||
|
|
||||||
|
File alert = new File(one, "onmouseclick='alert(oops)'");
|
||||||
|
FS.touch(alert);
|
||||||
|
|
||||||
if (!OS.IS_WINDOWS)
|
if (!OS.IS_WINDOWS)
|
||||||
{
|
{
|
||||||
assertTrue("Creating dir 'f??r' (Might not work in Windows)", new File(resBase, "f??r").mkdir());
|
assertTrue("Creating dir 'f??r' (Might not work in Windows)", new File(resBase, "f??r").mkdir());
|
||||||
|
@ -166,6 +170,15 @@ public class DefaultServletTest
|
||||||
String response = connector.getResponses(req1.toString());
|
String response = connector.getResponses(req1.toString());
|
||||||
|
|
||||||
assertResponseNotContains("<script>", response);
|
assertResponseNotContains("<script>", response);
|
||||||
|
|
||||||
|
|
||||||
|
req1 = new StringBuffer();
|
||||||
|
req1.append("GET /context/one/;\"onmouseover='alert(document.location)' HTTP/1.0\n");
|
||||||
|
req1.append("\n");
|
||||||
|
|
||||||
|
response = connector.getResponses(req1.toString());
|
||||||
|
|
||||||
|
assertResponseNotContains("\"onmouseover", response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -413,7 +426,7 @@ public class DefaultServletTest
|
||||||
assertTrue(index.delete());
|
assertTrue(index.delete());
|
||||||
response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
|
response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
|
||||||
assertResponseContains("Location: http://0.0.0.0/context/dir/index.htm", response);
|
assertResponseContains("Location: http://0.0.0.0/context/dir/index.htm", response);
|
||||||
|
|
||||||
assertTrue(inde.delete());
|
assertTrue(inde.delete());
|
||||||
response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
|
response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
|
||||||
assertResponseContains("403", response);
|
assertResponseContains("403", response);
|
||||||
|
@ -523,7 +536,7 @@ public class DefaultServletTest
|
||||||
if (!OS.IS_WINDOWS)
|
if (!OS.IS_WINDOWS)
|
||||||
{
|
{
|
||||||
context.clearAliasChecks();
|
context.clearAliasChecks();
|
||||||
|
|
||||||
Files.createSymbolicLink(dirLink.toPath(),dir.toPath());
|
Files.createSymbolicLink(dirLink.toPath(),dir.toPath());
|
||||||
Files.createSymbolicLink(dirRLink.toPath(),new File("dir").toPath());
|
Files.createSymbolicLink(dirRLink.toPath(),new File("dir").toPath());
|
||||||
Files.createSymbolicLink(link.toPath(),foobar.toPath());
|
Files.createSymbolicLink(link.toPath(),foobar.toPath());
|
||||||
|
@ -611,12 +624,12 @@ public class DefaultServletTest
|
||||||
{
|
{
|
||||||
if (!OS.IS_LINUX)
|
if (!OS.IS_LINUX)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
testdir.ensureEmpty();
|
testdir.ensureEmpty();
|
||||||
File resBase = testdir.getPathFile("docroot").toFile();
|
File resBase = testdir.getPathFile("docroot").toFile();
|
||||||
FS.ensureDirExists(resBase);
|
FS.ensureDirExists(resBase);
|
||||||
context.setBaseResource(Resource.newResource(resBase));
|
context.setBaseResource(Resource.newResource(resBase));
|
||||||
|
|
||||||
File index = new File(resBase, "index.html");
|
File index = new File(resBase, "index.html");
|
||||||
createFile(index, "<h1>Hello World</h1>");
|
createFile(index, "<h1>Hello World</h1>");
|
||||||
|
|
||||||
|
@ -632,19 +645,19 @@ public class DefaultServletTest
|
||||||
|
|
||||||
response = connector.getResponses("GET /context/index.html HTTP/1.0\r\n\r\n");
|
response = connector.getResponses("GET /context/index.html HTTP/1.0\r\n\r\n");
|
||||||
assertResponseContains("<h1>Hello World</h1>", response);
|
assertResponseContains("<h1>Hello World</h1>", response);
|
||||||
|
|
||||||
ResourceContentFactory factory = (ResourceContentFactory)context.getServletContext().getAttribute("resourceCache");
|
ResourceContentFactory factory = (ResourceContentFactory)context.getServletContext().getAttribute("resourceCache");
|
||||||
|
|
||||||
HttpContent content = factory.getContent("/index.html",200);
|
HttpContent content = factory.getContent("/index.html",200);
|
||||||
ByteBuffer buffer = content.getDirectBuffer();
|
ByteBuffer buffer = content.getDirectBuffer();
|
||||||
Assert.assertTrue(buffer.isDirect());
|
Assert.assertTrue(buffer.isDirect());
|
||||||
content = factory.getContent("/index.html",5);
|
content = factory.getContent("/index.html",5);
|
||||||
buffer = content.getDirectBuffer();
|
buffer = content.getDirectBuffer();
|
||||||
Assert.assertTrue(buffer==null);
|
Assert.assertTrue(buffer==null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRangeRequests() throws Exception
|
public void testRangeRequests() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -817,7 +830,7 @@ public class DefaultServletTest
|
||||||
server.stop();
|
server.stop();
|
||||||
context.addFilter(OutputFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
|
context.addFilter(OutputFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\n\r\n");
|
||||||
assertResponseContains("Content-Length: 2", response); // 20 something long
|
assertResponseContains("Content-Length: 2", response); // 20 something long
|
||||||
assertResponseContains("Extra Info", response);
|
assertResponseContains("Extra Info", response);
|
||||||
|
@ -828,7 +841,7 @@ public class DefaultServletTest
|
||||||
context.getServletHandler().setFilters(new FilterHolder[]{});
|
context.getServletHandler().setFilters(new FilterHolder[]{});
|
||||||
context.addFilter(WriterFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
|
context.addFilter(WriterFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\n\r\n");
|
||||||
assertResponseContains("Content-Length: 2", response); // 20 something long
|
assertResponseContains("Content-Length: 2", response); // 20 something long
|
||||||
assertResponseContains("Extra Info", response);
|
assertResponseContains("Extra Info", response);
|
||||||
|
@ -867,7 +880,7 @@ public class DefaultServletTest
|
||||||
int e=response.indexOf("ETag: ");
|
int e=response.indexOf("ETag: ");
|
||||||
String etag = response.substring(e+6,response.indexOf('"',e+11)+1);
|
String etag = response.substring(e+6,response.indexOf('"',e+11)+1);
|
||||||
String etag_gzip = etag.substring(0,etag.length()-1)+"--gzip\"";
|
String etag_gzip = etag.substring(0,etag.length()-1)+"--gzip\"";
|
||||||
|
|
||||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
|
||||||
assertResponseContains("Content-Length: 9", response);
|
assertResponseContains("Content-Length: 9", response);
|
||||||
assertResponseContains("fake gzip",response);
|
assertResponseContains("fake gzip",response);
|
||||||
|
@ -875,7 +888,7 @@ public class DefaultServletTest
|
||||||
assertResponseContains("Vary: Accept-Encoding",response);
|
assertResponseContains("Vary: Accept-Encoding",response);
|
||||||
assertResponseContains("Content-Encoding: gzip",response);
|
assertResponseContains("Content-Encoding: gzip",response);
|
||||||
assertResponseContains("ETag: "+etag_gzip,response);
|
assertResponseContains("ETag: "+etag_gzip,response);
|
||||||
|
|
||||||
response = connector.getResponses("GET /context/data0.txt.gz HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt.gz HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
|
||||||
assertResponseContains("Content-Length: 9", response);
|
assertResponseContains("Content-Length: 9", response);
|
||||||
assertResponseContains("fake gzip",response);
|
assertResponseContains("fake gzip",response);
|
||||||
|
@ -883,8 +896,8 @@ public class DefaultServletTest
|
||||||
assertResponseNotContains("Vary: Accept-Encoding",response);
|
assertResponseNotContains("Vary: Accept-Encoding",response);
|
||||||
assertResponseNotContains("Content-Encoding: gzip",response);
|
assertResponseNotContains("Content-Encoding: gzip",response);
|
||||||
assertResponseNotContains("ETag: "+etag_gzip,response);
|
assertResponseNotContains("ETag: "+etag_gzip,response);
|
||||||
assertResponseContains("ETag: ",response);
|
assertResponseContains("ETag: ",response);
|
||||||
|
|
||||||
response = connector.getResponses("GET /context/data0.txt.gz HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: W/\"wobble\"\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt.gz HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: W/\"wobble\"\r\n\r\n");
|
||||||
assertResponseContains("Content-Length: 9", response);
|
assertResponseContains("Content-Length: 9", response);
|
||||||
assertResponseContains("fake gzip",response);
|
assertResponseContains("fake gzip",response);
|
||||||
|
@ -893,7 +906,7 @@ public class DefaultServletTest
|
||||||
assertResponseNotContains("Content-Encoding: gzip",response);
|
assertResponseNotContains("Content-Encoding: gzip",response);
|
||||||
assertResponseNotContains("ETag: "+etag_gzip,response);
|
assertResponseNotContains("ETag: "+etag_gzip,response);
|
||||||
assertResponseContains("ETag: ",response);
|
assertResponseContains("ETag: ",response);
|
||||||
|
|
||||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: "+etag_gzip+"\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: "+etag_gzip+"\r\n\r\n");
|
||||||
assertResponseContains("304 Not Modified", response);
|
assertResponseContains("304 Not Modified", response);
|
||||||
assertResponseContains("ETag: "+etag_gzip,response);
|
assertResponseContains("ETag: "+etag_gzip,response);
|
||||||
|
@ -901,7 +914,7 @@ public class DefaultServletTest
|
||||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: "+etag+"\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: "+etag+"\r\n\r\n");
|
||||||
assertResponseContains("304 Not Modified", response);
|
assertResponseContains("304 Not Modified", response);
|
||||||
assertResponseContains("ETag: "+etag,response);
|
assertResponseContains("ETag: "+etag,response);
|
||||||
|
|
||||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: W/\"foobar\","+etag_gzip+"\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: W/\"foobar\","+etag_gzip+"\r\n\r\n");
|
||||||
assertResponseContains("304 Not Modified", response);
|
assertResponseContains("304 Not Modified", response);
|
||||||
assertResponseContains("ETag: "+etag_gzip,response);
|
assertResponseContains("ETag: "+etag_gzip,response);
|
||||||
|
@ -909,7 +922,7 @@ public class DefaultServletTest
|
||||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: W/\"foobar\","+etag+"\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: W/\"foobar\","+etag+"\r\n\r\n");
|
||||||
assertResponseContains("304 Not Modified", response);
|
assertResponseContains("304 Not Modified", response);
|
||||||
assertResponseContains("ETag: "+etag,response);
|
assertResponseContains("ETag: "+etag,response);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -934,7 +947,7 @@ public class DefaultServletTest
|
||||||
defholder.setInitParameter("resourceBase", resBasePath);
|
defholder.setInitParameter("resourceBase", resBasePath);
|
||||||
defholder.setInitParameter("maxCachedFiles", "1024");
|
defholder.setInitParameter("maxCachedFiles", "1024");
|
||||||
defholder.setInitParameter("maxCachedFileSize", "200000000");
|
defholder.setInitParameter("maxCachedFileSize", "200000000");
|
||||||
defholder.setInitParameter("maxCacheSize", "256000000");
|
defholder.setInitParameter("maxCacheSize", "256000000");
|
||||||
|
|
||||||
String response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\n\r\n");
|
String response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\n\r\n");
|
||||||
assertResponseContains("Content-Length: 12", response);
|
assertResponseContains("Content-Length: 12", response);
|
||||||
|
@ -946,7 +959,7 @@ public class DefaultServletTest
|
||||||
int e=response.indexOf("ETag: ");
|
int e=response.indexOf("ETag: ");
|
||||||
String etag = response.substring(e+6,response.indexOf('"',e+11)+1);
|
String etag = response.substring(e+6,response.indexOf('"',e+11)+1);
|
||||||
String etag_gzip = etag.substring(0,etag.length()-1)+"--gzip\"";
|
String etag_gzip = etag.substring(0,etag.length()-1)+"--gzip\"";
|
||||||
|
|
||||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
|
||||||
assertResponseContains("Content-Length: 9", response);
|
assertResponseContains("Content-Length: 9", response);
|
||||||
assertResponseContains("fake gzip",response);
|
assertResponseContains("fake gzip",response);
|
||||||
|
@ -954,7 +967,7 @@ public class DefaultServletTest
|
||||||
assertResponseContains("Vary: Accept-Encoding",response);
|
assertResponseContains("Vary: Accept-Encoding",response);
|
||||||
assertResponseContains("Content-Encoding: gzip",response);
|
assertResponseContains("Content-Encoding: gzip",response);
|
||||||
assertResponseContains("ETag: "+etag_gzip,response);
|
assertResponseContains("ETag: "+etag_gzip,response);
|
||||||
|
|
||||||
response = connector.getResponses("GET /context/data0.txt.gz HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt.gz HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\n\r\n");
|
||||||
assertResponseContains("Content-Length: 9", response);
|
assertResponseContains("Content-Length: 9", response);
|
||||||
assertResponseContains("fake gzip",response);
|
assertResponseContains("fake gzip",response);
|
||||||
|
@ -963,7 +976,7 @@ public class DefaultServletTest
|
||||||
assertResponseNotContains("Content-Encoding: gzip",response);
|
assertResponseNotContains("Content-Encoding: gzip",response);
|
||||||
assertResponseNotContains("ETag: "+etag_gzip,response);
|
assertResponseNotContains("ETag: "+etag_gzip,response);
|
||||||
assertResponseContains("ETag: ",response);
|
assertResponseContains("ETag: ",response);
|
||||||
|
|
||||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: "+etag_gzip+"\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: "+etag_gzip+"\r\n\r\n");
|
||||||
assertResponseContains("304 Not Modified", response);
|
assertResponseContains("304 Not Modified", response);
|
||||||
assertResponseContains("ETag: "+etag_gzip,response);
|
assertResponseContains("ETag: "+etag_gzip,response);
|
||||||
|
@ -971,7 +984,7 @@ public class DefaultServletTest
|
||||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: "+etag+"\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: "+etag+"\r\n\r\n");
|
||||||
assertResponseContains("304 Not Modified", response);
|
assertResponseContains("304 Not Modified", response);
|
||||||
assertResponseContains("ETag: "+etag,response);
|
assertResponseContains("ETag: "+etag,response);
|
||||||
|
|
||||||
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: W/\"foobar\","+etag_gzip+"\r\n\r\n");
|
response = connector.getResponses("GET /context/data0.txt HTTP/1.0\r\nHost:localhost:8080\r\nAccept-Encoding:gzip\r\nIf-None-Match: W/\"foobar\","+etag_gzip+"\r\n\r\n");
|
||||||
assertResponseContains("304 Not Modified", response);
|
assertResponseContains("304 Not Modified", response);
|
||||||
assertResponseContains("ETag: "+etag_gzip,response);
|
assertResponseContains("ETag: "+etag_gzip,response);
|
||||||
|
|
|
@ -30,19 +30,23 @@ import java.net.URL;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.B64Code;
|
import org.eclipse.jetty.util.B64Code;
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.util.IO;
|
||||||
import org.eclipse.jetty.util.Loader;
|
import org.eclipse.jetty.util.Loader;
|
||||||
|
import org.eclipse.jetty.util.MultiMap;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.URIUtil;
|
import org.eclipse.jetty.util.URIUtil;
|
||||||
import org.eclipse.jetty.util.UrlEncoded;
|
import org.eclipse.jetty.util.UrlEncoded;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
|
@ -340,7 +344,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* Time resource was last modified.
|
* Time resource was last modified.
|
||||||
*
|
*
|
||||||
* @return the last modified time as milliseconds since unix epoch
|
* @return the last modified time as milliseconds since unix epoch
|
||||||
*/
|
*/
|
||||||
public abstract long lastModified();
|
public abstract long lastModified();
|
||||||
|
@ -349,7 +353,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* Length of the resource.
|
* Length of the resource.
|
||||||
*
|
*
|
||||||
* @return the length of the resource
|
* @return the length of the resource
|
||||||
*/
|
*/
|
||||||
public abstract long length();
|
public abstract long length();
|
||||||
|
@ -358,7 +362,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* URL representing the resource.
|
* URL representing the resource.
|
||||||
*
|
*
|
||||||
* @return an URL representing the given resource
|
* @return an URL representing the given resource
|
||||||
* @deprecated use {{@link #getURI()}.toURL() instead.
|
* @deprecated use {{@link #getURI()}.toURL() instead.
|
||||||
*/
|
*/
|
||||||
|
@ -368,7 +372,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* URI representing the resource.
|
* URI representing the resource.
|
||||||
*
|
*
|
||||||
* @return an URI representing the given resource
|
* @return an URI representing the given resource
|
||||||
*/
|
*/
|
||||||
public URI getURI()
|
public URI getURI()
|
||||||
|
@ -387,10 +391,10 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* File representing the given resource.
|
* File representing the given resource.
|
||||||
*
|
*
|
||||||
* @return an File representing the given resource or NULL if this
|
* @return an File representing the given resource or NULL if this
|
||||||
* is not possible.
|
* is not possible.
|
||||||
* @throws IOException if unable to get the resource due to permissions
|
* @throws IOException if unable to get the resource due to permissions
|
||||||
*/
|
*/
|
||||||
public abstract File getFile()
|
public abstract File getFile()
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
@ -399,7 +403,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* The name of the resource.
|
* The name of the resource.
|
||||||
*
|
*
|
||||||
* @return the name of the resource
|
* @return the name of the resource
|
||||||
*/
|
*/
|
||||||
public abstract String getName();
|
public abstract String getName();
|
||||||
|
@ -408,7 +412,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* Input stream to the resource
|
* Input stream to the resource
|
||||||
*
|
*
|
||||||
* @return an input stream to the resource
|
* @return an input stream to the resource
|
||||||
* @throws IOException if unable to open the input stream
|
* @throws IOException if unable to open the input stream
|
||||||
*/
|
*/
|
||||||
|
@ -418,7 +422,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* Readable ByteChannel for the resource.
|
* Readable ByteChannel for the resource.
|
||||||
*
|
*
|
||||||
* @return an readable bytechannel to the resource or null if one is not available.
|
* @return an readable bytechannel to the resource or null if one is not available.
|
||||||
* @throws IOException if unable to open the readable bytechannel for the resource.
|
* @throws IOException if unable to open the readable bytechannel for the resource.
|
||||||
*/
|
*/
|
||||||
|
@ -430,7 +434,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
* Deletes the given resource
|
* Deletes the given resource
|
||||||
* @return true if resource was found and successfully deleted, false if resource didn't exist or was unable to
|
* @return true if resource was found and successfully deleted, false if resource didn't exist or was unable to
|
||||||
* be deleted.
|
* be deleted.
|
||||||
* @throws SecurityException if unable to delete due to permissions
|
* @throws SecurityException if unable to delete due to permissions
|
||||||
*/
|
*/
|
||||||
public abstract boolean delete()
|
public abstract boolean delete()
|
||||||
throws SecurityException;
|
throws SecurityException;
|
||||||
|
@ -448,7 +452,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* list of resource names contained in the given resource.
|
* list of resource names contained in the given resource.
|
||||||
*
|
*
|
||||||
* @return a list of resource names contained in the given resource.
|
* @return a list of resource names contained in the given resource.
|
||||||
* Note: The resource names are not URL encoded.
|
* Note: The resource names are not URL encoded.
|
||||||
*/
|
*/
|
||||||
|
@ -523,7 +527,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
{
|
{
|
||||||
return getAlias()!=null;
|
return getAlias()!=null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @return The canonical Alias of this resource or null if none.
|
* @return The canonical Alias of this resource or null if none.
|
||||||
|
@ -540,65 +544,229 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
* @return String of HTML
|
* @return String of HTML
|
||||||
* @throws IOException if unable to get the list of resources as HTML
|
* @throws IOException if unable to get the list of resources as HTML
|
||||||
*/
|
*/
|
||||||
public String getListHTML(String base,boolean parent)
|
public String getListHTML(String base, boolean parent) throws IOException
|
||||||
throws IOException
|
{
|
||||||
|
return getListHTML(base, parent, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the resource list as a HTML directory listing.
|
||||||
|
* @param base The base URL
|
||||||
|
* @param parent True if the parent directory should be included
|
||||||
|
* @param query query params
|
||||||
|
* @return String of HTML
|
||||||
|
*/
|
||||||
|
public String getListHTML(String base, boolean parent, String query) throws IOException
|
||||||
{
|
{
|
||||||
base=URIUtil.canonicalPath(base);
|
base=URIUtil.canonicalPath(base);
|
||||||
if (base==null || !isDirectory())
|
if (base==null || !isDirectory())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
String[] ls = list();
|
|
||||||
if (ls==null)
|
|
||||||
return null;
|
|
||||||
Arrays.sort(ls);
|
|
||||||
|
|
||||||
String decodedBase = URIUtil.decodePath(base);
|
|
||||||
String title = "Directory: "+deTag(decodedBase);
|
|
||||||
|
|
||||||
StringBuilder buf=new StringBuilder(4096);
|
String[] rawListing = list();
|
||||||
buf.append("<HTML><HEAD>");
|
if (rawListing == null)
|
||||||
buf.append("<LINK HREF=\"").append("jetty-dir.css").append("\" REL=\"stylesheet\" TYPE=\"text/css\"/><TITLE>");
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean sortOrderAscending = true;
|
||||||
|
String sortColumn = "N"; // name (or "M" for Last Modified, or "S" for Size)
|
||||||
|
|
||||||
|
// check for query
|
||||||
|
if (query != null)
|
||||||
|
{
|
||||||
|
MultiMap<String> params = new MultiMap<>();
|
||||||
|
byte[] rawQuery = query.getBytes(UTF_8);
|
||||||
|
UrlEncoded.decodeUtf8To(rawQuery, 0, query.length(), params);
|
||||||
|
|
||||||
|
String paramO = params.getString("O");
|
||||||
|
String paramC = params.getString("C");
|
||||||
|
if (StringUtil.isNotBlank(paramO))
|
||||||
|
{
|
||||||
|
if (paramO.equals("A"))
|
||||||
|
{
|
||||||
|
sortOrderAscending = true;
|
||||||
|
}
|
||||||
|
else if (paramO.equals("D"))
|
||||||
|
{
|
||||||
|
sortOrderAscending = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (StringUtil.isNotBlank(paramC))
|
||||||
|
{
|
||||||
|
if (paramC.equals("N") || paramC.equals("M") || paramC.equals("S"))
|
||||||
|
{
|
||||||
|
sortColumn = paramC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather up entries
|
||||||
|
List<Resource> items = new ArrayList<>();
|
||||||
|
for (String l : rawListing)
|
||||||
|
{
|
||||||
|
Resource item = addPath(l);
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform sort
|
||||||
|
if (sortColumn.equals("M"))
|
||||||
|
{
|
||||||
|
Collections.sort(items, ResourceCollators.byLastModified(sortOrderAscending));
|
||||||
|
}
|
||||||
|
else if (sortColumn.equals("S"))
|
||||||
|
{
|
||||||
|
Collections.sort(items, ResourceCollators.bySize(sortOrderAscending));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Collections.sort(items, ResourceCollators.byName(sortOrderAscending));
|
||||||
|
}
|
||||||
|
|
||||||
|
String decodedBase = URIUtil.decodePath(base);
|
||||||
|
String title = "Directory: " + deTag(decodedBase);
|
||||||
|
|
||||||
|
StringBuilder buf = new StringBuilder(4096);
|
||||||
|
|
||||||
|
// Doctype Declaration (HTML5)
|
||||||
|
buf.append("<!DOCTYPE html>\n");
|
||||||
|
buf.append("<html lang=\"en\">\n");
|
||||||
|
|
||||||
|
// HTML Header
|
||||||
|
buf.append("<head>\n");
|
||||||
|
buf.append("<meta charset=\"utf-8\">\n");
|
||||||
|
buf.append("<link href=\"jetty-dir.css\" rel=\"stylesheet\" />\n");
|
||||||
|
buf.append("<title>");
|
||||||
buf.append(title);
|
buf.append(title);
|
||||||
buf.append("</TITLE></HEAD><BODY>\n<H1>");
|
buf.append("</title>\n");
|
||||||
buf.append(title);
|
buf.append("</head>\n");
|
||||||
buf.append("</H1>\n<TABLE BORDER=0>\n");
|
|
||||||
|
// HTML Body
|
||||||
|
buf.append("<body>\n");
|
||||||
|
buf.append("<h1 class=\"title\">").append(title).append("</h1>\n");
|
||||||
|
|
||||||
|
// HTML Table
|
||||||
|
final String ARROW_DOWN = " ⇩";
|
||||||
|
final String ARROW_UP = " ⇧";
|
||||||
|
String arrow;
|
||||||
|
String order;
|
||||||
|
|
||||||
|
buf.append("<table class=\"listing\">\n");
|
||||||
|
buf.append("<thead>\n");
|
||||||
|
|
||||||
|
arrow = "";
|
||||||
|
order = "A";
|
||||||
|
if (sortColumn.equals("N"))
|
||||||
|
{
|
||||||
|
if(sortOrderAscending)
|
||||||
|
{
|
||||||
|
order = "D";
|
||||||
|
arrow = ARROW_UP;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
order = "A";
|
||||||
|
arrow = ARROW_DOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.append("<tr><th class=\"name\"><a href=\"?C=N&O=").append(order).append("\">");
|
||||||
|
buf.append("Name").append(arrow);
|
||||||
|
buf.append("</a></th>");
|
||||||
|
|
||||||
|
arrow = "";
|
||||||
|
order = "A";
|
||||||
|
if (sortColumn.equals("M"))
|
||||||
|
{
|
||||||
|
if(sortOrderAscending)
|
||||||
|
{
|
||||||
|
order = "D";
|
||||||
|
arrow = ARROW_UP;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
order = "A";
|
||||||
|
arrow = ARROW_DOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.append("<th class=\"lastmodified\"><a href=\"?C=M&O=").append(order).append("\">");
|
||||||
|
buf.append("Last Modified").append(arrow);
|
||||||
|
buf.append("</a></th>");
|
||||||
|
|
||||||
|
arrow = "";
|
||||||
|
order = "A";
|
||||||
|
if (sortColumn.equals("S"))
|
||||||
|
{
|
||||||
|
if(sortOrderAscending)
|
||||||
|
{
|
||||||
|
order = "D";
|
||||||
|
arrow = ARROW_UP;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
order = "A";
|
||||||
|
arrow = ARROW_DOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.append("<th class=\"size\"><a href=\"?C=S&O=").append(order).append("\">");
|
||||||
|
buf.append("Size").append(arrow);
|
||||||
|
buf.append("</a></th></tr>\n");
|
||||||
|
buf.append("</thead>\n");
|
||||||
|
|
||||||
|
buf.append("<tbody>\n");
|
||||||
|
|
||||||
|
String encodedBase = hrefEncodeURI(base);
|
||||||
|
|
||||||
if (parent)
|
if (parent)
|
||||||
{
|
{
|
||||||
buf.append("<TR><TD><A HREF=\"");
|
// Name
|
||||||
buf.append(URIUtil.addEncodedPaths(base,"../"));
|
buf.append("<tr><td class=\"name\"><a href=\"");
|
||||||
buf.append("\">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
|
buf.append(URIUtil.addPaths(encodedBase,"../"));
|
||||||
|
buf.append("\">Parent Directory</a></td>");
|
||||||
|
// Last Modified
|
||||||
|
buf.append("<td class=\"lastmodified\">-</td>");
|
||||||
|
// Size
|
||||||
|
buf.append("<td>-</td>");
|
||||||
|
buf.append("</tr>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
String encodedBase = hrefEncodeURI(base);
|
|
||||||
|
|
||||||
DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
|
DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
|
||||||
DateFormat.MEDIUM);
|
DateFormat.MEDIUM);
|
||||||
for (int i=0 ; i< ls.length ; i++)
|
for (Resource item: items)
|
||||||
{
|
{
|
||||||
Resource item = addPath(ls[i]);
|
String name = item.getName();
|
||||||
|
int slashIdx = name.lastIndexOf('/');
|
||||||
buf.append("\n<TR><TD><A HREF=\"");
|
if (slashIdx != -1)
|
||||||
String path=URIUtil.addEncodedPaths(encodedBase,URIUtil.encodePath(ls[i]));
|
{
|
||||||
|
name = name.substring(slashIdx + 1);
|
||||||
|
}
|
||||||
|
if (item.isDirectory() && !name.endsWith("/"))
|
||||||
|
{
|
||||||
|
name += URIUtil.SLASH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name
|
||||||
|
buf.append("<tr><td class=\"name\"><a href=\"");
|
||||||
|
String path=URIUtil.addPaths(encodedBase,URIUtil.encodePath(name));
|
||||||
buf.append(path);
|
buf.append(path);
|
||||||
|
|
||||||
if (item.isDirectory() && !path.endsWith("/"))
|
|
||||||
buf.append(URIUtil.SLASH);
|
|
||||||
|
|
||||||
// URIUtil.encodePath(buf,path);
|
|
||||||
buf.append("\">");
|
buf.append("\">");
|
||||||
buf.append(deTag(ls[i]));
|
buf.append(deTag(name));
|
||||||
buf.append(" ");
|
buf.append(" ");
|
||||||
buf.append("</A></TD><TD ALIGN=right>");
|
buf.append("</a></td>");
|
||||||
buf.append(item.length());
|
|
||||||
buf.append(" bytes </TD><TD>");
|
// Last Modified
|
||||||
|
buf.append("<td class=\"lastmodified\">");
|
||||||
buf.append(dfmt.format(new Date(item.lastModified())));
|
buf.append(dfmt.format(new Date(item.lastModified())));
|
||||||
buf.append("</TD></TR>");
|
buf.append("</td>");
|
||||||
|
|
||||||
|
// Size
|
||||||
|
buf.append("<td class=\"size\">");
|
||||||
|
buf.append(String.format("%,d", item.length()));
|
||||||
|
buf.append(" bytes </td></tr>\n");
|
||||||
}
|
}
|
||||||
buf.append("</TABLE>\n");
|
buf.append("</tbody>\n");
|
||||||
buf.append("</BODY></HTML>\n");
|
buf.append("</table>\n");
|
||||||
|
buf.append("</body></html>\n");
|
||||||
|
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -666,7 +834,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @param out the output stream to write to
|
* @param out the output stream to write to
|
||||||
* @param start First byte to write
|
* @param start First byte to write
|
||||||
* @param count Bytes to write or -1 for all of them.
|
* @param count Bytes to write or -1 for all of them.
|
||||||
* @throws IOException if unable to copy the Resource to the output
|
* @throws IOException if unable to copy the Resource to the output
|
||||||
|
@ -689,7 +857,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
* Copy the Resource to the new destination file.
|
* Copy the Resource to the new destination file.
|
||||||
* <p>
|
* <p>
|
||||||
* Will not replace existing destination file.
|
* Will not replace existing destination file.
|
||||||
*
|
*
|
||||||
* @param destination the destination file to create
|
* @param destination the destination file to create
|
||||||
* @throws IOException if unable to copy the resource
|
* @throws IOException if unable to copy the resource
|
||||||
*/
|
*/
|
||||||
|
@ -698,7 +866,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
{
|
{
|
||||||
if (destination.exists())
|
if (destination.exists())
|
||||||
throw new IllegalArgumentException(destination + " exists");
|
throw new IllegalArgumentException(destination + " exists");
|
||||||
|
|
||||||
try (OutputStream out = new FileOutputStream(destination))
|
try (OutputStream out = new FileOutputStream(destination))
|
||||||
{
|
{
|
||||||
writeTo(out,0,-1);
|
writeTo(out,0,-1);
|
||||||
|
@ -708,14 +876,14 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* Generate a weak ETag reference for this Resource.
|
* Generate a weak ETag reference for this Resource.
|
||||||
*
|
*
|
||||||
* @return the weak ETag reference for this resource.
|
* @return the weak ETag reference for this resource.
|
||||||
*/
|
*/
|
||||||
public String getWeakETag()
|
public String getWeakETag()
|
||||||
{
|
{
|
||||||
return getWeakETag("");
|
return getWeakETag("");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getWeakETag(String suffix)
|
public String getWeakETag(String suffix)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.util.resource;
|
||||||
|
|
||||||
|
import java.text.Collator;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class ResourceCollators
|
||||||
|
{
|
||||||
|
private static Comparator<? super Resource> BY_NAME_ASCENDING =
|
||||||
|
new Comparator<Resource>()
|
||||||
|
{
|
||||||
|
private final Collator collator = Collator.getInstance(Locale.ENGLISH);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(Resource o1, Resource o2)
|
||||||
|
{
|
||||||
|
return collator.compare(o1.getName(), o2.getName());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static Comparator<? super Resource> BY_NAME_DESCENDING =
|
||||||
|
Collections.reverseOrder(BY_NAME_ASCENDING);
|
||||||
|
|
||||||
|
private static Comparator<? super Resource> BY_LAST_MODIFIED_ASCENDING =
|
||||||
|
new Comparator<Resource>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public int compare(Resource o1, Resource o2)
|
||||||
|
{
|
||||||
|
return Long.compare(o1.lastModified(), o2.lastModified());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static Comparator<? super Resource> BY_LAST_MODIFIED_DESCENDING =
|
||||||
|
Collections.reverseOrder(BY_LAST_MODIFIED_ASCENDING);
|
||||||
|
|
||||||
|
private static Comparator<? super Resource> BY_SIZE_ASCENDING =
|
||||||
|
new Comparator<Resource>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public int compare(Resource o1, Resource o2)
|
||||||
|
{
|
||||||
|
return Long.compare(o1.length(), o2.length());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static Comparator<? super Resource> BY_SIZE_DESCENDING =
|
||||||
|
Collections.reverseOrder(BY_SIZE_ASCENDING);
|
||||||
|
|
||||||
|
|
||||||
|
public static Comparator<? super Resource> byLastModified(boolean sortOrderAscending)
|
||||||
|
{
|
||||||
|
if (sortOrderAscending)
|
||||||
|
{
|
||||||
|
return BY_LAST_MODIFIED_ASCENDING;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return BY_LAST_MODIFIED_DESCENDING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Comparator<? super Resource> byName(boolean sortOrderAscending)
|
||||||
|
{
|
||||||
|
if (sortOrderAscending)
|
||||||
|
{
|
||||||
|
return BY_NAME_ASCENDING;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return BY_NAME_DESCENDING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Comparator<? super Resource> bySize(boolean sortOrderAscending)
|
||||||
|
{
|
||||||
|
if (sortOrderAscending)
|
||||||
|
{
|
||||||
|
return BY_SIZE_ASCENDING;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return BY_SIZE_DESCENDING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,38 @@
|
||||||
body
|
body {
|
||||||
{
|
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
font-family: sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1
|
h1.title {
|
||||||
{
|
|
||||||
text-shadow: #000000 -1px -1px 1px;
|
text-shadow: #000000 -1px -1px 1px;
|
||||||
color: #FC390E;
|
color: #FC390E;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
a
|
table.listing {
|
||||||
{
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead a {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th {
|
||||||
|
border-bottom: black 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name, .lastmodified {
|
||||||
|
text-align: left;
|
||||||
|
padding-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
color: #7036be;
|
color: #7036be;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
@ -21,10 +40,9 @@ a
|
||||||
font-size:inherit;
|
font-size:inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
td
|
td {
|
||||||
{
|
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
padding: 2px 15px 2px 0px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue