Merged branch 'jetty9.3.x' into 'master'.
This commit is contained in:
commit
f727e2bac3
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue