Merged branch 'jetty9.3.x' into 'master'.

This commit is contained in:
Simone Bordet 2016-02-13 11:32:53 +01:00
commit f727e2bac3
6 changed files with 486 additions and 332 deletions

View File

@ -418,7 +418,8 @@ public class PushCacheFilterTest extends AbstractTest
public void testRecursivePush() throws Exception
{
final String primaryResource = "/primary.html";
final String secondaryResource = "/secondary.css";
final String secondaryResource1 = "/secondary1.css";
final String secondaryResource2 = "/secondary2.js";
final String tertiaryResource = "/tertiary.png";
start(new HttpServlet()
{
@ -429,8 +430,10 @@ public class PushCacheFilterTest extends AbstractTest
final ServletOutputStream output = response.getOutputStream();
if (requestURI.endsWith(primaryResource))
output.print("<html><head></head><body>PRIMARY</body></html>");
else if (requestURI.endsWith(secondaryResource))
else if (requestURI.endsWith(secondaryResource1))
output.print("body { background-image: url(\"" + tertiaryResource + "\"); }");
else if (requestURI.endsWith(secondaryResource2))
output.print("(function() { window.alert('HTTP/2'); })()");
if (requestURI.endsWith(tertiaryResource))
output.write("TERTIARY".getBytes(StandardCharsets.UTF_8));
}
@ -442,7 +445,7 @@ public class PushCacheFilterTest extends AbstractTest
final String primaryURI = "http://localhost:" + connector.getLocalPort() + servletPath + primaryResource;
HttpFields primaryFields = new HttpFields();
MetaData.Request primaryRequest = newRequest("GET", primaryResource, primaryFields);
final CountDownLatch warmupLatch = new CountDownLatch(1);
final CountDownLatch warmupLatch = new CountDownLatch(2);
session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
{
@Override
@ -451,12 +454,12 @@ public class PushCacheFilterTest extends AbstractTest
callback.succeeded();
if (frame.isEndStream())
{
// Request for the secondary resource.
final String secondaryURI = "http://localhost:" + connector.getLocalPort() + servletPath + secondaryResource;
HttpFields secondaryFields = new HttpFields();
secondaryFields.put(HttpHeader.REFERER, primaryURI);
MetaData.Request secondaryRequest = newRequest("GET", secondaryResource, secondaryFields);
session.newStream(new HeadersFrame(secondaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
// Request for the secondary resources.
String secondaryURI1 = "http://localhost:" + connector.getLocalPort() + servletPath + secondaryResource1;
HttpFields secondaryFields1 = new HttpFields();
secondaryFields1.put(HttpHeader.REFERER, primaryURI);
MetaData.Request secondaryRequest1 = newRequest("GET", secondaryResource1, secondaryFields1);
session.newStream(new HeadersFrame(secondaryRequest1, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
@ -466,13 +469,14 @@ public class PushCacheFilterTest extends AbstractTest
{
// Request for the tertiary resource.
HttpFields tertiaryFields = new HttpFields();
tertiaryFields.put(HttpHeader.REFERER, secondaryURI);
tertiaryFields.put(HttpHeader.REFERER, secondaryURI1);
MetaData.Request tertiaryRequest = newRequest("GET", tertiaryResource, tertiaryFields);
session.newStream(new HeadersFrame(tertiaryRequest, null, true), new Promise.Adapter<>(), new Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
callback.succeeded();
if (frame.isEndStream())
warmupLatch.countDown();
}
@ -480,6 +484,20 @@ public class PushCacheFilterTest extends AbstractTest
}
}
});
HttpFields secondaryFields2 = new HttpFields();
secondaryFields2.put(HttpHeader.REFERER, primaryURI);
MetaData.Request secondaryRequest2 = newRequest("GET", secondaryResource2, secondaryFields2);
session.newStream(new HeadersFrame(secondaryRequest2, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
callback.succeeded();
if (frame.isEndStream())
warmupLatch.countDown();
}
});
}
}
});
@ -487,19 +505,73 @@ public class PushCacheFilterTest extends AbstractTest
Thread.sleep(1000);
// Request again the primary resource, we should get the secondary and tertiary resource pushed.
// Request again the primary resource, we should get the secondary and tertiary resources pushed.
primaryRequest = newRequest("GET", primaryResource, primaryFields);
final CountDownLatch primaryResponseLatch = new CountDownLatch(1);
final CountDownLatch pushLatch = new CountDownLatch(2);
final CountDownLatch primaryPushesLatch = new CountDownLatch(3);
final CountDownLatch recursiveLatch = new CountDownLatch(1);
session.newStream(new HeadersFrame(primaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
callback.succeeded();
if (frame.isEndStream())
primaryResponseLatch.countDown();
}
@Override
public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
{
// The stream id of the PUSH_PROMISE must
// always be a client stream and therefore odd.
Assert.assertEquals(1, frame.getStreamId() & 1);
return new Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
callback.succeeded();
if (frame.isEndStream())
primaryPushesLatch.countDown();
}
@Override
public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
{
return new Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
callback.succeeded();
if (frame.isEndStream())
recursiveLatch.countDown();
}
};
}
};
}
});
Assert.assertTrue(primaryPushesLatch.await(5, TimeUnit.SECONDS));
Assert.assertFalse(recursiveLatch.await(1, TimeUnit.SECONDS));
Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
// Make sure that explicitly requesting a secondary resource, we get the tertiary pushed.
CountDownLatch secondaryResponseLatch = new CountDownLatch(1);
CountDownLatch secondaryPushLatch = new CountDownLatch(1);
MetaData.Request secondaryRequest = newRequest("GET", secondaryResource1, new HttpFields());
session.newStream(new HeadersFrame(secondaryRequest, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
{
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
callback.succeeded();
if (frame.isEndStream())
secondaryResponseLatch.countDown();
}
@Override
public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
{
@ -510,19 +582,14 @@ public class PushCacheFilterTest extends AbstractTest
{
callback.succeeded();
if (frame.isEndStream())
pushLatch.countDown();
}
@Override
public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
{
return this;
secondaryPushLatch.countDown();
}
};
}
});
Assert.assertTrue(pushLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(primaryResponseLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(secondaryPushLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(secondaryResponseLatch.await(5, TimeUnit.SECONDS));
}
@Test

View File

@ -123,6 +123,11 @@ public class AttributeNormalizer
return 1;
}
if( (o1.path == null) && (o2.path == null) )
{
return 0;
}
// Different lengths?
int diff = o2.path.getNameCount() - o1.path.getNameCount();
if(diff != 0)
@ -142,14 +147,19 @@ public class AttributeNormalizer
}
}
private URI warURI;
private List<PathAttribute> attributes = new ArrayList<>();
public AttributeNormalizer(Resource baseResource)
{
// WAR URI is always evaluated before paths.
warURI = baseResource == null ? null : baseResource.getURI();
// We don't normalize or resolve the baseResource URI
if (!warURI.isAbsolute())
throw new IllegalArgumentException("WAR URI is not absolute: " + warURI);
try
{
// Track path attributes for expansion
attributes.add(new PathAttribute("WAR", baseResource == null ? null : baseResource.getFile().toPath()).weight(10));
attributes.add(new PathAttribute("jetty.base", "jetty.base").weight(9));
attributes.add(new PathAttribute("jetty.home", "jetty.home").weight(8));
attributes.add(new PathAttribute("user.home", "user.home").weight(7));
@ -195,7 +205,13 @@ public class AttributeNormalizer
{
return "file:" + normalizePath(new File(uri).toPath());
}
else
{
if(uri.isAbsolute())
{
return normalizeUri(uri);
}
}
}
catch (Exception e)
{
@ -203,6 +219,17 @@ public class AttributeNormalizer
}
return String.valueOf(o);
}
public String normalizeUri(URI uri)
{
String uriStr = uri.toASCIIString();
String warStr = warURI.toASCIIString();
if (uriStr.startsWith(warStr))
{
return "${WAR}" + uriStr.substring(warStr.length());
}
return uriStr;
}
public String normalizePath(Path path)
{
@ -306,7 +333,13 @@ public class AttributeNormalizer
private String getString(String property)
{
// Use known attributes first
// Use war path (if known)
if("WAR".equalsIgnoreCase(property))
{
return warURI.toASCIIString();
}
// Use known path attributes
for (PathAttribute attr : attributes)
{
if (attr.key.equalsIgnoreCase(property))

View File

@ -16,14 +16,15 @@
// ========================================================================
//
package org.eclipse.jetty.servlets;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
@ -153,7 +154,7 @@ public class PushCacheFilter implements Filter
}
if (LOG.isDebugEnabled())
LOG.debug("{} {} referrer={} conditional={}", request.getMethod(), request.getRequestURI(), referrer, conditional);
LOG.debug("{} {} referrer={} conditional={} synthetic={}", request.getMethod(), request.getRequestURI(), referrer, conditional, isPushRequest(request));
String path = URIUtil.addPaths(request.getServletPath(), request.getPathInfo());
String query = request.getQueryString();
@ -190,11 +191,11 @@ public class PushCacheFilter implements Filter
{
if (now - primaryTimestamp < TimeUnit.MILLISECONDS.toNanos(_associatePeriod))
{
ConcurrentMap<String, String> associated = primaryResource._associated;
Set<String> associated = primaryResource._associated;
// Not strictly concurrent-safe, just best effort to limit associations.
if (associated.size() <= _maxAssociations)
{
if (associated.putIfAbsent(path, path) == null)
if (associated.add(path))
{
if (LOG.isDebugEnabled())
LOG.debug("Associated {} to {}", path, referrerPathNoContext);
@ -229,13 +230,12 @@ public class PushCacheFilter implements Filter
}
}
// Push some resources?
PrimaryResource primaryResource = _cache.get(path);
if (primaryResource == null)
{
PrimaryResource t = new PrimaryResource();
primaryResource = _cache.putIfAbsent(path, t);
primaryResource = primaryResource == null ? t : primaryResource;
PrimaryResource r = new PrimaryResource();
primaryResource = _cache.putIfAbsent(path, r);
primaryResource = primaryResource == null ? r : primaryResource;
primaryResource._timestamp.compareAndSet(0, now);
if (LOG.isDebugEnabled())
LOG.debug("Cached primary resource {}", path);
@ -251,23 +251,38 @@ public class PushCacheFilter implements Filter
}
}
// Push associated for non conditional
if (!conditional && !primaryResource._associated.isEmpty())
// Push associated resources.
if (!isPushRequest(request) && !conditional && !primaryResource._associated.isEmpty())
{
PushBuilder builder = Request.getBaseRequest(request).getPushBuilder();
PushBuilder pushBuilder = Request.getBaseRequest(request).getPushBuilder();
for (String associated : primaryResource._associated.values())
// Breadth-first push of associated resources.
Queue<PrimaryResource> queue = new ArrayDeque<>();
queue.offer(primaryResource);
while (!queue.isEmpty())
{
if (LOG.isDebugEnabled())
LOG.debug("Pushing {} for {}", associated, path);
PrimaryResource parent = queue.poll();
for (String childPath : parent._associated)
{
PrimaryResource child = _cache.get(childPath);
if (child != null)
queue.offer(child);
builder.path(associated).push();
if (LOG.isDebugEnabled())
LOG.debug("Pushing {} for {}", childPath, path);
pushBuilder.path(childPath).push();
}
}
}
chain.doFilter(request, resp);
}
private boolean isPushRequest(HttpServletRequest request)
{
return Boolean.TRUE.equals(request.getAttribute("org.eclipse.jetty.pushed"));
}
@Override
public void destroy()
{
@ -281,7 +296,7 @@ public class PushCacheFilter implements Filter
for (Map.Entry<String, PrimaryResource> entry : _cache.entrySet())
{
PrimaryResource resource = entry.getValue();
String value = String.format("size=%d: %s", resource._associated.size(), new TreeSet<>(resource._associated.keySet()));
String value = String.format("size=%d: %s", resource._associated.size(), new TreeSet<>(resource._associated));
result.put(entry.getKey(), value);
}
return result;
@ -301,7 +316,7 @@ public class PushCacheFilter implements Filter
private static class PrimaryResource
{
private final ConcurrentMap<String, String> _associated = new ConcurrentHashMap<>();
private final Set<String> _associated = Collections.newSetFromMap(new ConcurrentHashMap<>());
private final AtomicLong _timestamp = new AtomicLong();
}
}

View File

@ -16,10 +16,11 @@
// ========================================================================
//
package org.eclipse.jetty.servlets;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@ -41,29 +42,20 @@ import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/**
*/
public class PushSessionCacheFilter implements Filter
{
private static final String TARGET_ATTR="PushCacheFilter.target";
private static final String TIMESTAMP_ATTR="PushCacheFilter.timestamp";
private static final String TARGET_ATTR = "PushCacheFilter.target";
private static final String TIMESTAMP_ATTR = "PushCacheFilter.timestamp";
private static final Logger LOG = Log.getLogger(PushSessionCacheFilter.class);
private final ConcurrentMap<String, Target> _cache = new ConcurrentHashMap<>();
private long _associateDelay=5000L;
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
private long _associateDelay = 5000L;
@Override
public void init(FilterConfig config) throws ServletException
{
if (config.getInitParameter("associateDelay")!=null)
_associateDelay=Long.valueOf(config.getInitParameter("associateDelay"));
if (config.getInitParameter("associateDelay") != null)
_associateDelay = Long.valueOf(config.getInitParameter("associateDelay"));
// Add a listener that is used to collect information about associated resource,
// etags and modified dates
config.getServletContext().addListener(new ServletRequestListener()
@ -74,45 +66,47 @@ public class PushSessionCacheFilter implements Filter
{
Request request = Request.getBaseRequest(sre.getServletRequest());
Target target = (Target)request.getAttribute(TARGET_ATTR);
if (target==null)
if (target == null)
return;
// Update conditional data
Response response = request.getResponse();
target._etag=response.getHttpFields().get(HttpHeader.ETAG);
target._lastModified=response.getHttpFields().get(HttpHeader.LAST_MODIFIED);
target._etag = response.getHttpFields().get(HttpHeader.ETAG);
target._lastModified = response.getHttpFields().get(HttpHeader.LAST_MODIFIED);
// Don't associate pushes
if (request.isPush())
{
if (LOG.isDebugEnabled())
LOG.debug("Pushed {} for {}",request.getResponse().getStatus(),request.getRequestURI());
LOG.debug("Pushed {} for {}", request.getResponse().getStatus(), request.getRequestURI());
return;
}
}
else if (LOG.isDebugEnabled())
LOG.debug("Served {} for {}",request.getResponse().getStatus(),request.getRequestURI());
{
LOG.debug("Served {} for {}", request.getResponse().getStatus(), request.getRequestURI());
}
// Does this request have a referer?
String referer = request.getHttpFields().get(HttpHeader.REFERER);
if (referer!=null)
if (referer != null)
{
// Is the referer from this contexts?
HttpURI referer_uri = new HttpURI(referer);
if (request.getServerName().equals(referer_uri.getHost()))
{
Target referer_target = _cache.get(referer_uri.getPath());
if (referer_target!=null)
if (referer_target != null)
{
HttpSession session = request.getSession();
HttpSession session = request.getSession();
ConcurrentHashMap<String, Long> timestamps = (ConcurrentHashMap<String, Long>)session.getAttribute(TIMESTAMP_ATTR);
Long last = timestamps.get(referer_target._path);
if (last!=null && (System.currentTimeMillis()-last)<_associateDelay)
if (last != null && (System.currentTimeMillis() - last) < _associateDelay)
{
if (referer_target._associated.putIfAbsent(target._path,target)==null)
if (referer_target._associated.putIfAbsent(target._path, target) == null)
{
if (LOG.isDebugEnabled())
LOG.debug("ASSOCIATE {}->{}",referer_target._path,target._path);
LOG.debug("ASSOCIATE {}->{}", referer_target._path, target._path);
}
}
}
@ -124,24 +118,18 @@ public class PushSessionCacheFilter implements Filter
public void requestInitialized(ServletRequestEvent sre)
{
}
});
}
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
{
// Get Jetty request as these APIs are not yet standard
Request baseRequest = Request.getBaseRequest(request);
String uri=baseRequest.getRequestURI();
String uri = baseRequest.getRequestURI();
if (LOG.isDebugEnabled())
LOG.debug("{} {} push={}",baseRequest.getMethod(),uri,baseRequest.isPush());
LOG.debug("{} {} push={}", baseRequest.getMethod(), uri, baseRequest.isPush());
HttpSession session = baseRequest.getSession(true);
@ -149,66 +137,70 @@ public class PushSessionCacheFilter implements Filter
Target target = _cache.get(uri);
if (target == null)
{
Target t=new Target(uri);
target = _cache.putIfAbsent(uri,t);
target = target==null?t:target;
Target t = new Target(uri);
target = _cache.putIfAbsent(uri, t);
target = target == null ? t : target;
}
request.setAttribute(TARGET_ATTR,target);
request.setAttribute(TARGET_ATTR, target);
// Set the timestamp for this resource in this session
ConcurrentHashMap<String, Long> timestamps = (ConcurrentHashMap<String, Long>)session.getAttribute(TIMESTAMP_ATTR);
if (timestamps==null)
if (timestamps == null)
{
timestamps=new ConcurrentHashMap<>();
session.setAttribute(TIMESTAMP_ATTR,timestamps);
timestamps = new ConcurrentHashMap<>();
session.setAttribute(TIMESTAMP_ATTR, timestamps);
}
timestamps.put(uri,System.currentTimeMillis());
timestamps.put(uri, System.currentTimeMillis());
// push any associated resources
if (baseRequest.isPushSupported() && target._associated.size()>0)
if (baseRequest.isPushSupported() && !baseRequest.isPush() && !target._associated.isEmpty())
{
PushBuilder builder = baseRequest.getPushBuilder();
builder.addHeader("X-Pusher",PushSessionCacheFilter.class.toString());
for (Target associated : target._associated.values())
// Breadth-first push of associated resources.
Queue<Target> queue = new ArrayDeque<>();
queue.offer(target);
while (!queue.isEmpty())
{
String path = associated._path;
if (LOG.isDebugEnabled())
LOG.debug("PUSH {} <- {}",path,uri);
builder.path(path).etag(associated._etag).lastModified(associated._lastModified).push();
Target parent = queue.poll();
PushBuilder builder = baseRequest.getPushBuilder();
builder.addHeader("X-Pusher", PushSessionCacheFilter.class.toString());
for (Target child : parent._associated.values())
{
queue.offer(child);
String path = child._path;
if (LOG.isDebugEnabled())
LOG.debug("PUSH {} <- {}", path, uri);
builder.path(path).etag(child._etag).lastModified(child._lastModified).push();
}
}
}
chain.doFilter(request,response);
chain.doFilter(request, response);
}
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.Filter#destroy()
*/
@Override
public void destroy()
{
{
_cache.clear();
}
public static class Target
private static class Target
{
final String _path;
final ConcurrentMap<String,Target> _associated = new ConcurrentHashMap<>();
volatile String _etag;
volatile String _lastModified;
public Target(String path)
private final String _path;
private final ConcurrentMap<String, Target> _associated = new ConcurrentHashMap<>();
private volatile String _etag;
private volatile String _lastModified;
private Target(String path)
{
_path=path;
_path = path;
}
@Override
public String toString()
{
return String.format("Target{p=%s,e=%s,m=%s,a=%d}",_path,_etag,_lastModified,_associated.size());
return String.format("Target{p=%s,e=%s,m=%s,a=%d}", _path, _etag, _lastModified, _associated.size());
}
}
}

View File

@ -0,0 +1,244 @@
//
// ========================================================================
// Copyright (c) 1995-2016 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.quickstart;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class AttributeNormalizerPathTest
{
@Parameters(name="{0} = {1}")
public static List<String[]> data()
{
String[][] tests = {
// Can't test 'WAR' property, as its not a Path (which this testcase works with)
// { "WAR", toSystemPath("http://localhost/resources/webapps/root") },
{ "jetty.home", toSystemPath("/opt/jetty-distro") },
{ "jetty.base", toSystemPath("/opt/jetty-distro/demo.base") },
{ "user.home", toSystemPath("/home/user") },
{ "user.dir", toSystemPath("/etc/init.d") },
};
return Arrays.asList(tests);
}
/**
* As the declared paths in this testcase might be actual paths on the system
* running these tests, the expected paths should be cleaned up to represent
* the actual system paths.
* <p>
* Eg: on fedora /etc/init.d is a symlink to /etc/rc.d/init.d
*/
private static String toSystemPath(String rawpath)
{
Path path = FileSystems.getDefault().getPath(rawpath);
if (Files.exists(path))
{
// It exists, resolve it to the real path
try
{
path = path.toRealPath();
}
catch (IOException e)
{
// something prevented us from resolving to real path, fallback to
// absolute path resolution (not as accurate)
path = path.toAbsolutePath();
e.printStackTrace();
}
}
else
{
// File doesn't exist, resolve to absolute path
// We can't rely on File.toCanonicalPath() here
path = path.toAbsolutePath();
}
return path.toString();
}
private static String origJettyBase;
private static String origJettyHome;
private static String origUserHome;
private static String origUserDir;
@BeforeClass
public static void initProperties()
{
origJettyBase = System.getProperty("jetty.base");
origJettyHome = System.getProperty("jetty.home");
origUserHome = System.getProperty("user.home");
origUserDir = System.getProperty("user.dir");
System.setProperty("jetty.home","/opt/jetty-distro");
System.setProperty("jetty.base","/opt/jetty-distro/demo.base");
System.setProperty("user.home","/home/user");
System.setProperty("user.dir","/etc/init.d");
}
@AfterClass
public static void restoreProperties()
{
if(origJettyBase != null) System.setProperty("jetty.base",origJettyBase);
if(origJettyHome != null) System.setProperty("jetty.home",origJettyHome);
if(origUserHome != null) System.setProperty("user.home",origUserHome);
if(origUserDir != null) System.setProperty("user.dir",origUserDir);
}
@Parameter(0)
public String key;
@Parameter(1)
public String path;
private AttributeNormalizer normalizer;
public AttributeNormalizerPathTest() throws MalformedURLException
{
normalizer = new AttributeNormalizer(Resource.newResource("/opt/jetty-distro/demo.base/webapps/root"));
}
@Test
public void testEqual()
{
assertThat(normalizer.normalize("file:" + path), is("file:${" + key + "}"));
}
@Test
public void testEqualsSlash()
{
assertThat(normalizer.normalize("file:" + path + "/"), is("file:${" + key + "}"));
}
@Test
public void testEqualsSlashFile()
{
assertThat(normalizer.normalize("file:" + path + "/file"), is("file:${" + key + "}/file"));
}
@Test
public void testURIEquals() throws URISyntaxException
{
assertThat(normalizer.normalize(new URI("file:" + path)), is("file:${" + key + "}"));
}
@Test
public void testURIEqualsSlash() throws URISyntaxException
{
assertThat(normalizer.normalize(new URI("file:" + path + "/")), is("file:${" + key + "}"));
}
@Test
public void testURIEqualsSlashFile() throws URISyntaxException
{
assertThat(normalizer.normalize(new URI("file:" + path + "/file")), is("file:${" + key + "}/file"));
}
@Test
public void testURLEquals() throws MalformedURLException
{
assertThat(normalizer.normalize(new URL("file:" + path)), is("file:${" + key + "}"));
}
@Test
public void testURLEqualsSlash() throws MalformedURLException
{
assertThat(normalizer.normalize(new URL("file:" + path + "/")), is("file:${" + key + "}"));
}
@Test
public void testURLEqualsSlashFile() throws MalformedURLException
{
assertThat(normalizer.normalize(new URL("file:" + path + "/file")), is("file:${" + key + "}/file"));
}
@Test
public void testJarFileEquals_BangFile()
{
assertThat(normalizer.normalize("jar:file:" + path + "!/file"), is("jar:file:${" + key + "}!/file"));
}
@Test
public void testJarFileEquals_SlashBangFile()
{
assertThat(normalizer.normalize("jar:file:" + path + "/!/file"), is("jar:file:${" + key + "}!/file"));
}
@Test
public void testJarFileEquals_FileBangFile()
{
assertThat(normalizer.normalize("jar:file:" + path + "/file!/file"), is("jar:file:${" + key + "}/file!/file"));
}
@Test
public void testJarFileEquals_URIBangFile() throws URISyntaxException
{
assertThat(normalizer.normalize(new URI("jar:file:" + path + "!/file")), is("jar:file:${" + key + "}!/file"));
}
@Test
public void testJarFileEquals_URISlashBangFile() throws URISyntaxException
{
assertThat(normalizer.normalize(new URI("jar:file:" + path + "/!/file")), is("jar:file:${" + key + "}!/file"));
}
@Test
public void testJarFileEquals_URIFileBangFile() throws URISyntaxException
{
assertThat(normalizer.normalize(new URI("jar:file:" + path + "/file!/file")), is("jar:file:${" + key + "}/file!/file"));
}
@Test
public void testJarFileEquals_URLBangFile() throws MalformedURLException
{
assertThat(normalizer.normalize(new URL("jar:file:" + path + "!/file")), is("jar:file:${" + key + "}!/file"));
}
@Test
public void testJarFileEquals_URLSlashBangFile() throws MalformedURLException
{
assertThat(normalizer.normalize(new URL("jar:file:" + path + "/!/file")), is("jar:file:${" + key + "}!/file"));
}
@Test
public void testJarFileEquals_URLFileBangFile() throws MalformedURLException
{
assertThat(normalizer.normalize(new URL("jar:file:" + path + "/file!/file")), is("jar:file:${" + key + "}/file!/file"));
}
}

View File

@ -1,4 +1,4 @@
//
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
@ -18,226 +18,29 @@
package org.eclipse.jetty.quickstart;
import java.net.MalformedURLException;
import java.net.URI;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.Test;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class AttributeNormalizerTest
{
@Parameters(name="{0} = {1}")
public static List<String[]> data()
{
String[][] tests = {
{ "WAR", toSystemPath("/opt/jetty-distro/demo.base/webapps/root") },
{ "jetty.home", toSystemPath("/opt/jetty-distro") },
{ "jetty.base", toSystemPath("/opt/jetty-distro/demo.base") },
{ "user.home", toSystemPath("/home/user") },
{ "user.dir", toSystemPath("/etc/init.d") },
};
return Arrays.asList(tests);
}
/**
* As the declared paths in this testcase might be actual paths on the system
* running these tests, the expected paths should be cleaned up to represent
* the actual system paths.
* <p>
* Eg: on fedora /etc/init.d is a symlink to /etc/rc.d/init.d
*/
private static String toSystemPath(String rawpath)
{
Path path = FileSystems.getDefault().getPath(rawpath);
if (Files.exists(path))
{
// It exists, resolve it to the real path
try
{
path = path.toRealPath();
}
catch (IOException e)
{
// something prevented us from resolving to real path, fallback to
// absolute path resolution (not as accurate)
path = path.toAbsolutePath();
e.printStackTrace();
}
}
else
{
// File doesn't exist, resolve to absolute path
// We can't rely on File.toCanonicalPath() here
path = path.toAbsolutePath();
}
return path.toString();
}
private static String origJettyBase;
private static String origJettyHome;
private static String origUserHome;
private static String origUserDir;
@BeforeClass
public static void initProperties()
{
origJettyBase = System.getProperty("jetty.base");
origJettyHome = System.getProperty("jetty.home");
origUserHome = System.getProperty("user.home");
origUserDir = System.getProperty("user.dir");
System.setProperty("jetty.home","/opt/jetty-distro");
System.setProperty("jetty.base","/opt/jetty-distro/demo.base");
System.setProperty("user.home","/home/user");
System.setProperty("user.dir","/etc/init.d");
}
@AfterClass
public static void restoreProperties()
{
if(origJettyBase != null) System.setProperty("jetty.base",origJettyBase);
if(origJettyHome != null) System.setProperty("jetty.home",origJettyHome);
if(origUserHome != null) System.setProperty("user.home",origUserHome);
if(origUserDir != null) System.setProperty("user.dir",origUserDir);
}
@Parameter(0)
public String key;
@Parameter(1)
public String path;
private AttributeNormalizer normalizer;
public AttributeNormalizerTest() throws MalformedURLException
{
normalizer = new AttributeNormalizer(Resource.newResource("/opt/jetty-distro/demo.base/webapps/root"));
}
@Test
public void testEqual()
public void testNormalizeWAR() throws MalformedURLException
{
assertThat(normalizer.normalize("file:" + path), is("file:${" + key + "}"));
}
String webref = "http://localhost/resource/webapps/root";
Resource webresource = Resource.newResource(webref);
AttributeNormalizer normalizer = new AttributeNormalizer(webresource);
String result = null;
@Test
public void testEqualsSlash()
{
assertThat(normalizer.normalize("file:" + path + "/"), is("file:${" + key + "}"));
}
result = normalizer.normalize(URI.create(webref));
assertThat(result, is("${WAR}"));
@Test
public void testEqualsSlashFile()
{
assertThat(normalizer.normalize("file:" + path + "/file"), is("file:${" + key + "}/file"));
}
@Test
public void testURIEquals() throws URISyntaxException
{
assertThat(normalizer.normalize(new URI("file:" + path)), is("file:${" + key + "}"));
}
@Test
public void testURIEqualsSlash() throws URISyntaxException
{
assertThat(normalizer.normalize(new URI("file:" + path + "/")), is("file:${" + key + "}"));
}
@Test
public void testURIEqualsSlashFile() throws URISyntaxException
{
assertThat(normalizer.normalize(new URI("file:" + path + "/file")), is("file:${" + key + "}/file"));
}
@Test
public void testURLEquals() throws MalformedURLException
{
assertThat(normalizer.normalize(new URL("file:" + path)), is("file:${" + key + "}"));
}
@Test
public void testURLEqualsSlash() throws MalformedURLException
{
assertThat(normalizer.normalize(new URL("file:" + path + "/")), is("file:${" + key + "}"));
}
@Test
public void testURLEqualsSlashFile() throws MalformedURLException
{
assertThat(normalizer.normalize(new URL("file:" + path + "/file")), is("file:${" + key + "}/file"));
}
@Test
public void testJarFileEquals_BangFile()
{
assertThat(normalizer.normalize("jar:file:" + path + "!/file"), is("jar:file:${" + key + "}!/file"));
}
@Test
public void testJarFileEquals_SlashBangFile()
{
assertThat(normalizer.normalize("jar:file:" + path + "/!/file"), is("jar:file:${" + key + "}!/file"));
}
@Test
public void testJarFileEquals_FileBangFile()
{
assertThat(normalizer.normalize("jar:file:" + path + "/file!/file"), is("jar:file:${" + key + "}/file!/file"));
}
@Test
public void testJarFileEquals_URIBangFile() throws URISyntaxException
{
assertThat(normalizer.normalize(new URI("jar:file:" + path + "!/file")), is("jar:file:${" + key + "}!/file"));
}
@Test
public void testJarFileEquals_URISlashBangFile() throws URISyntaxException
{
assertThat(normalizer.normalize(new URI("jar:file:" + path + "/!/file")), is("jar:file:${" + key + "}!/file"));
}
@Test
public void testJarFileEquals_URIFileBangFile() throws URISyntaxException
{
assertThat(normalizer.normalize(new URI("jar:file:" + path + "/file!/file")), is("jar:file:${" + key + "}/file!/file"));
}
@Test
public void testJarFileEquals_URLBangFile() throws MalformedURLException
{
assertThat(normalizer.normalize(new URL("jar:file:" + path + "!/file")), is("jar:file:${" + key + "}!/file"));
}
@Test
public void testJarFileEquals_URLSlashBangFile() throws MalformedURLException
{
assertThat(normalizer.normalize(new URL("jar:file:" + path + "/!/file")), is("jar:file:${" + key + "}!/file"));
}
@Test
public void testJarFileEquals_URLFileBangFile() throws MalformedURLException
{
assertThat(normalizer.normalize(new URL("jar:file:" + path + "/file!/file")), is("jar:file:${" + key + "}/file!/file"));
result = normalizer.normalize(URI.create(webref + "/deep/ref"));
assertThat(result, is("${WAR}/deep/ref"));
}
}