This commit is contained in:
Greg Wilkins 2017-04-20 11:38:28 +10:00
parent f5631a9f1b
commit 2f04b0f869
16 changed files with 211 additions and 64 deletions

View File

@ -111,8 +111,7 @@ public class FastFileServer
{
if (!request.getPathInfo().endsWith(URIUtil.SLASH))
{
response.sendRedirect(response.encodeRedirectURL(URIUtil
.addPaths(request.getRequestURI(), URIUtil.SLASH)));
response.sendRedirect(response.encodeRedirectURL(request.getRequestURI()+URIUtil.SLASH));
return;
}
String listing = Resource.newResource(file).getListHTML(

View File

@ -54,7 +54,7 @@ public final class RedirectUtil
// relative to request
String path = request.getRequestURI();
String parent = (path.endsWith("/")) ? path : URIUtil.parentPath(path);
location = URIUtil.canonicalPath(URIUtil.addPaths(parent,location));
location = URIUtil.canonicalPath(URIUtil.addEncodedPaths(parent,location));
if (!location.startsWith("/"))
url.append('/');
}

View File

@ -297,7 +297,7 @@ public class PushBuilderImpl implements PushBuilder
_fields.add(HttpHeader.IF_MODIFIED_SINCE,_lastModified);
}
HttpURI uri = HttpURI.createHttpURI(_request.getScheme(),_request.getServerName(),_request.getServerPort(),_path,param,query,null);
HttpURI uri = HttpURI.createHttpURI(_request.getScheme(),_request.getServerName(),_request.getServerPort(),path,param,query,null);
MetaData.Request push = new MetaData.Request(_method,uri,_request.getHttpVersion(),_fields);
if (LOG.isDebugEnabled())

View File

@ -1279,7 +1279,7 @@ public class Request implements HttpServletRequest
relTo = relTo.substring(0,slash + 1);
else
relTo = "/";
path = URIUtil.addPaths(URIUtil.encodePath(relTo),path);
path = URIUtil.addPaths(relTo,path);
}
return _context.getRequestDispatcher(path);

View File

@ -648,7 +648,7 @@ public class Response implements HttpServletResponse
// relative to request
String path=_channel.getRequest().getRequestURI();
String parent=(path.endsWith("/"))?path:URIUtil.parentPath(path);
location=URIUtil.canonicalPath(URIUtil.addPaths(parent,location));
location=URIUtil.canonicalPath(URIUtil.addEncodedPaths(parent,location));
if (!location.startsWith("/"))
buf.append('/');
}

View File

@ -563,7 +563,7 @@ public class Server extends HandlerWrapper implements Attributes
// this is a dispatch with a path
ServletContext context=event.getServletContext();
String query=baseRequest.getQueryString();
baseRequest.setURIPathQuery(URIUtil.addPaths(context==null?null:URIUtil.encodePath(context.getContextPath()), path));
baseRequest.setURIPathQuery(URIUtil.addEncodedPaths(context==null?null:URIUtil.encodePath(context.getContextPath()), path));
HttpURI uri = baseRequest.getHttpURI();
baseRequest.setPathInfo(uri.getDecodedPath());
if (uri.getQuery()!=null)

View File

@ -996,9 +996,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
// context request must end with /
baseRequest.setHandled(true);
if (baseRequest.getQueryString() != null)
response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH) + "?" + baseRequest.getQueryString());
response.sendRedirect(baseRequest.getRequestURI() + "/?" + baseRequest.getQueryString());
else
response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH));
response.sendRedirect(baseRequest.getRequestURI() + "/");
return false;
}

View File

@ -453,7 +453,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory
boolean endsWithSlash=(pathInfo==null?request.getServletPath():pathInfo).endsWith(URIUtil.SLASH);
if (!endsWithSlash)
{
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH)));
response.sendRedirect(response.encodeRedirectURL(request.getRequestURI()+URIUtil.SLASH));
return;
}

View File

@ -490,9 +490,10 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
{
String q=request.getQueryString();
pathInContext=pathInContext.substring(0,pathInContext.length()-1);
String uri = URIUtil.addPaths(_servletContext.getContextPath(),pathInContext);
if (q!=null&&q.length()!=0)
pathInContext+="?"+q;
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(_servletContext.getContextPath(),pathInContext)));
uri+="?"+q;
response.sendRedirect(response.encodeRedirectURL(uri));
return;
}
@ -578,11 +579,11 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
{
// Redirect to the index
response.setContentLength(0);
String uri=URIUtil.encodePath(URIUtil.addPaths( _servletContext.getContextPath(),welcome));
String q=request.getQueryString();
if (q!=null&&q.length()!=0)
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome)+"?"+q));
else
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome)));
if (q!=null&&!q.isEmpty())
uri+="?"+q;
response.sendRedirect(response.encodeRedirectURL(uri));
}
else
{

View File

@ -411,7 +411,32 @@ public class DefaultServletTest
response = connector.getResponses("GET /context/dir/ HTTP/1.0\r\n\r\n");
assertResponseContains("403", response);
}
@Test
public void testWelcomeDirWithQuestion() throws Exception
{
testdir.ensureEmpty();
File resBase = testdir.getPathFile("docroot").toFile();
FS.ensureDirExists(resBase);
context.setBaseResource(Resource.newResource(resBase));
File dir = new File(resBase, "dir?");
assertTrue(dir.mkdirs());
File index = new File(dir, "index.html");
createFile(index, "<h1>Hello Index</h1>");
ServletHolder defholder = context.addServlet(DefaultServlet.class, "/");
defholder.setInitParameter("dirAllowed", "false");
defholder.setInitParameter("redirectWelcome", "true");
defholder.setInitParameter("welcomeServlets", "false");
defholder.setInitParameter("gzip", "false");
String response = connector.getResponse("GET /context/dir%3F HTTP/1.0\r\n\r\n");
assertResponseContains("Location: http://0.0.0.0/context/dir%3F/", response);
response = connector.getResponse("GET /context/dir%3F/ HTTP/1.0\r\n\r\n");
assertResponseContains("Location: http://0.0.0.0/context/dir%3F/index.html", response);
}
@Test

View File

@ -472,14 +472,14 @@ public class URIUtil
/* ------------------------------------------------------------ */
/** Add two URI path segments.
/** Add two encoded URI path segments.
* Handles null and empty paths, path and query params (eg ?a=b or
* ;JSESSIONID=xxx) and avoids duplicate '/'
* @param p1 URI path segment (should be encoded)
* @param p2 URI path segment (should be encoded)
* @return Legally combined path segments.
*/
public static String addPaths(String p1, String p2)
public static String addEncodedPaths(String p1, String p2)
{
if (p1==null || p1.length()==0)
{
@ -524,7 +524,54 @@ public class URIUtil
return buf.toString();
}
/* ------------------------------------------------------------ */
/** Add two Decoded URI path segments.
* Handles null and empty paths. Path and query params (eg ?a=b or
* ;JSESSIONID=xxx) are not handled
* @param p1 URI path segment (should be decoded)
* @param p2 URI path segment (should be decoded)
* @return Legally combined path segments.
*/
public static String addPaths(String p1, String p2)
{
if (p1==null || p1.length()==0)
{
if (p1!=null && p2==null)
return p1;
return p2;
}
if (p2==null || p2.length()==0)
return p1;
boolean p1EndsWithSlash = p1.endsWith(SLASH);
boolean p2StartsWithSlash = p2.startsWith(SLASH);
if (p1EndsWithSlash && p2StartsWithSlash)
{
if (p2.length()==1)
return p1;
if (p1.length()==1)
return p2;
}
StringBuilder buf = new StringBuilder(p1.length()+p2.length()+2);
buf.append(p1);
if (p1.endsWith(SLASH))
{
if (p2.startsWith(SLASH))
buf.setLength(buf.length()-1);
}
else
{
if (!p2.startsWith(SLASH))
buf.append(SLASH);
}
buf.append(p2);
return buf.toString();
}
/* ------------------------------------------------------------ */
/** Return the parent Path.
* Treat a URI like a directory path and return the parent directory.
@ -916,7 +963,12 @@ public class URIUtil
return equalsIgnoreEncodings(uriA.getPath(),uriB.getPath());
}
public static URI addDecodedPath(URI uri, String path)
/**
* @param uri A URI to add the path to
* @param path A decoded path element
* @return URI with path added.
*/
public static URI addPath(URI uri, String path)
{
String base = uri.toASCIIString();
StringBuilder buf = new StringBuilder(base.length()+path.length()*3);
@ -924,7 +976,6 @@ public class URIUtil
if (buf.charAt(base.length()-1)!='/')
buf.append('/');
byte[] bytes=null;
int offset=path.charAt(0)=='/'?1:0;
encodePath(buf,path,offset);

View File

@ -169,7 +169,7 @@ public class FileResource extends Resource
if (base.isDirectory())
{
// treat all paths being added as relative
uri=new URI(URIUtil.addPaths(base.toURI().toASCIIString(),encoded));
uri=new URI(URIUtil.addEncodedPaths(base.toURI().toASCIIString(),encoded));
}
else
{

View File

@ -213,7 +213,7 @@ public class PathResource extends Resource
this.path = parent.path.getFileSystem().getPath(parent.path.toString(), childPath);
if (isDirectory() &&!childPath.endsWith("/"))
childPath+="/";
this.uri = URIUtil.addDecodedPath(parent.uri,childPath);
this.uri = URIUtil.addPath(parent.uri,childPath);
this.alias = checkAliasPath();
}

View File

@ -566,7 +566,7 @@ public abstract class Resource implements ResourceFactory, Closeable
if (parent)
{
buf.append("<TR><TD><A HREF=\"");
buf.append(URIUtil.addPaths(base,"../"));
buf.append(URIUtil.addEncodedPaths(base,"../"));
buf.append("\">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
}
@ -579,7 +579,7 @@ public abstract class Resource implements ResourceFactory, Closeable
Resource item = addPath(ls[i]);
buf.append("\n<TR><TD><A HREF=\"");
String path=URIUtil.addPaths(encodedBase,URIUtil.encodePath(ls[i]));
String path=URIUtil.addEncodedPaths(encodedBase,URIUtil.encodePath(ls[i]));
buf.append(path);

View File

@ -303,7 +303,7 @@ public class URLResource extends Resource
path = URIUtil.canonicalPath(path);
return newResource(URIUtil.addPaths(_url.toExternalForm(),URIUtil.encodePath(path)), _useCaches);
return newResource(URIUtil.addEncodedPaths(_url.toExternalForm(),URIUtil.encodePath(path)), _useCaches);
}
/* ------------------------------------------------------------ */

View File

@ -87,7 +87,96 @@ public class URIUtilTest
/* ------------------------------------------------------------ */
@Test
public void testAddPaths()
public void testAddEncodedPaths()
{
assertEquals("null+null", URIUtil.addEncodedPaths(null,null),null);
assertEquals("null+", URIUtil.addEncodedPaths(null,""),"");
assertEquals("null+bbb", URIUtil.addEncodedPaths(null,"bbb"),"bbb");
assertEquals("null+/", URIUtil.addEncodedPaths(null,"/"),"/");
assertEquals("null+/bbb", URIUtil.addEncodedPaths(null,"/bbb"),"/bbb");
assertEquals("+null", URIUtil.addEncodedPaths("",null),"");
assertEquals("+", URIUtil.addEncodedPaths("",""),"");
assertEquals("+bbb", URIUtil.addEncodedPaths("","bbb"),"bbb");
assertEquals("+/", URIUtil.addEncodedPaths("","/"),"/");
assertEquals("+/bbb", URIUtil.addEncodedPaths("","/bbb"),"/bbb");
assertEquals("aaa+null", URIUtil.addEncodedPaths("aaa",null),"aaa");
assertEquals("aaa+", URIUtil.addEncodedPaths("aaa",""),"aaa");
assertEquals("aaa+bbb", URIUtil.addEncodedPaths("aaa","bbb"),"aaa/bbb");
assertEquals("aaa+/", URIUtil.addEncodedPaths("aaa","/"),"aaa/");
assertEquals("aaa+/bbb", URIUtil.addEncodedPaths("aaa","/bbb"),"aaa/bbb");
assertEquals("/+null", URIUtil.addEncodedPaths("/",null),"/");
assertEquals("/+", URIUtil.addEncodedPaths("/",""),"/");
assertEquals("/+bbb", URIUtil.addEncodedPaths("/","bbb"),"/bbb");
assertEquals("/+/", URIUtil.addEncodedPaths("/","/"),"/");
assertEquals("/+/bbb", URIUtil.addEncodedPaths("/","/bbb"),"/bbb");
assertEquals("aaa/+null", URIUtil.addEncodedPaths("aaa/",null),"aaa/");
assertEquals("aaa/+", URIUtil.addEncodedPaths("aaa/",""),"aaa/");
assertEquals("aaa/+bbb", URIUtil.addEncodedPaths("aaa/","bbb"),"aaa/bbb");
assertEquals("aaa/+/", URIUtil.addEncodedPaths("aaa/","/"),"aaa/");
assertEquals("aaa/+/bbb", URIUtil.addEncodedPaths("aaa/","/bbb"),"aaa/bbb");
assertEquals(";JS+null", URIUtil.addEncodedPaths(";JS",null),";JS");
assertEquals(";JS+", URIUtil.addEncodedPaths(";JS",""),";JS");
assertEquals(";JS+bbb", URIUtil.addEncodedPaths(";JS","bbb"),"bbb;JS");
assertEquals(";JS+/", URIUtil.addEncodedPaths(";JS","/"),"/;JS");
assertEquals(";JS+/bbb", URIUtil.addEncodedPaths(";JS","/bbb"),"/bbb;JS");
assertEquals("aaa;JS+null", URIUtil.addEncodedPaths("aaa;JS",null),"aaa;JS");
assertEquals("aaa;JS+", URIUtil.addEncodedPaths("aaa;JS",""),"aaa;JS");
assertEquals("aaa;JS+bbb", URIUtil.addEncodedPaths("aaa;JS","bbb"),"aaa/bbb;JS");
assertEquals("aaa;JS+/", URIUtil.addEncodedPaths("aaa;JS","/"),"aaa/;JS");
assertEquals("aaa;JS+/bbb", URIUtil.addEncodedPaths("aaa;JS","/bbb"),"aaa/bbb;JS");
assertEquals("aaa;JS+null", URIUtil.addEncodedPaths("aaa/;JS",null),"aaa/;JS");
assertEquals("aaa;JS+", URIUtil.addEncodedPaths("aaa/;JS",""),"aaa/;JS");
assertEquals("aaa;JS+bbb", URIUtil.addEncodedPaths("aaa/;JS","bbb"),"aaa/bbb;JS");
assertEquals("aaa;JS+/", URIUtil.addEncodedPaths("aaa/;JS","/"),"aaa/;JS");
assertEquals("aaa;JS+/bbb", URIUtil.addEncodedPaths("aaa/;JS","/bbb"),"aaa/bbb;JS");
assertEquals("?A=1+null", URIUtil.addEncodedPaths("?A=1",null),"?A=1");
assertEquals("?A=1+", URIUtil.addEncodedPaths("?A=1",""),"?A=1");
assertEquals("?A=1+bbb", URIUtil.addEncodedPaths("?A=1","bbb"),"bbb?A=1");
assertEquals("?A=1+/", URIUtil.addEncodedPaths("?A=1","/"),"/?A=1");
assertEquals("?A=1+/bbb", URIUtil.addEncodedPaths("?A=1","/bbb"),"/bbb?A=1");
assertEquals("aaa?A=1+null", URIUtil.addEncodedPaths("aaa?A=1",null),"aaa?A=1");
assertEquals("aaa?A=1+", URIUtil.addEncodedPaths("aaa?A=1",""),"aaa?A=1");
assertEquals("aaa?A=1+bbb", URIUtil.addEncodedPaths("aaa?A=1","bbb"),"aaa/bbb?A=1");
assertEquals("aaa?A=1+/", URIUtil.addEncodedPaths("aaa?A=1","/"),"aaa/?A=1");
assertEquals("aaa?A=1+/bbb", URIUtil.addEncodedPaths("aaa?A=1","/bbb"),"aaa/bbb?A=1");
assertEquals("aaa?A=1+null", URIUtil.addEncodedPaths("aaa/?A=1",null),"aaa/?A=1");
assertEquals("aaa?A=1+", URIUtil.addEncodedPaths("aaa/?A=1",""),"aaa/?A=1");
assertEquals("aaa?A=1+bbb", URIUtil.addEncodedPaths("aaa/?A=1","bbb"),"aaa/bbb?A=1");
assertEquals("aaa?A=1+/", URIUtil.addEncodedPaths("aaa/?A=1","/"),"aaa/?A=1");
assertEquals("aaa?A=1+/bbb", URIUtil.addEncodedPaths("aaa/?A=1","/bbb"),"aaa/bbb?A=1");
assertEquals(";JS?A=1+null", URIUtil.addEncodedPaths(";JS?A=1",null),";JS?A=1");
assertEquals(";JS?A=1+", URIUtil.addEncodedPaths(";JS?A=1",""),";JS?A=1");
assertEquals(";JS?A=1+bbb", URIUtil.addEncodedPaths(";JS?A=1","bbb"),"bbb;JS?A=1");
assertEquals(";JS?A=1+/", URIUtil.addEncodedPaths(";JS?A=1","/"),"/;JS?A=1");
assertEquals(";JS?A=1+/bbb", URIUtil.addEncodedPaths(";JS?A=1","/bbb"),"/bbb;JS?A=1");
assertEquals("aaa;JS?A=1+null", URIUtil.addEncodedPaths("aaa;JS?A=1",null),"aaa;JS?A=1");
assertEquals("aaa;JS?A=1+", URIUtil.addEncodedPaths("aaa;JS?A=1",""),"aaa;JS?A=1");
assertEquals("aaa;JS?A=1+bbb", URIUtil.addEncodedPaths("aaa;JS?A=1","bbb"),"aaa/bbb;JS?A=1");
assertEquals("aaa;JS?A=1+/", URIUtil.addEncodedPaths("aaa;JS?A=1","/"),"aaa/;JS?A=1");
assertEquals("aaa;JS?A=1+/bbb", URIUtil.addEncodedPaths("aaa;JS?A=1","/bbb"),"aaa/bbb;JS?A=1");
assertEquals("aaa;JS?A=1+null", URIUtil.addEncodedPaths("aaa/;JS?A=1",null),"aaa/;JS?A=1");
assertEquals("aaa;JS?A=1+", URIUtil.addEncodedPaths("aaa/;JS?A=1",""),"aaa/;JS?A=1");
assertEquals("aaa;JS?A=1+bbb", URIUtil.addEncodedPaths("aaa/;JS?A=1","bbb"),"aaa/bbb;JS?A=1");
assertEquals("aaa;JS?A=1+/", URIUtil.addEncodedPaths("aaa/;JS?A=1","/"),"aaa/;JS?A=1");
assertEquals("aaa;JS?A=1+/bbb", URIUtil.addEncodedPaths("aaa/;JS?A=1","/bbb"),"aaa/bbb;JS?A=1");
}
/* ------------------------------------------------------------ */
@Test
public void testAddDecodedPaths()
{
assertEquals("null+null", URIUtil.addPaths(null,null),null);
assertEquals("null+", URIUtil.addPaths(null,""),"");
@ -121,57 +210,39 @@ public class URIUtilTest
assertEquals(";JS+null", URIUtil.addPaths(";JS",null),";JS");
assertEquals(";JS+", URIUtil.addPaths(";JS",""),";JS");
assertEquals(";JS+bbb", URIUtil.addPaths(";JS","bbb"),"bbb;JS");
assertEquals(";JS+/", URIUtil.addPaths(";JS","/"),"/;JS");
assertEquals(";JS+/bbb", URIUtil.addPaths(";JS","/bbb"),"/bbb;JS");
assertEquals(";JS+bbb", URIUtil.addPaths(";JS","bbb"),";JS/bbb");
assertEquals(";JS+/", URIUtil.addPaths(";JS","/"),";JS/");
assertEquals(";JS+/bbb", URIUtil.addPaths(";JS","/bbb"),";JS/bbb");
assertEquals("aaa;JS+null", URIUtil.addPaths("aaa;JS",null),"aaa;JS");
assertEquals("aaa;JS+", URIUtil.addPaths("aaa;JS",""),"aaa;JS");
assertEquals("aaa;JS+bbb", URIUtil.addPaths("aaa;JS","bbb"),"aaa/bbb;JS");
assertEquals("aaa;JS+/", URIUtil.addPaths("aaa;JS","/"),"aaa/;JS");
assertEquals("aaa;JS+/bbb", URIUtil.addPaths("aaa;JS","/bbb"),"aaa/bbb;JS");
assertEquals("aaa;JS+bbb", URIUtil.addPaths("aaa;JS","bbb"),"aaa;JS/bbb");
assertEquals("aaa;JS+/", URIUtil.addPaths("aaa;JS","/"),"aaa;JS/");
assertEquals("aaa;JS+/bbb", URIUtil.addPaths("aaa;JS","/bbb"),"aaa;JS/bbb");
assertEquals("aaa;JS+null", URIUtil.addPaths("aaa/;JS",null),"aaa/;JS");
assertEquals("aaa;JS+", URIUtil.addPaths("aaa/;JS",""),"aaa/;JS");
assertEquals("aaa;JS+bbb", URIUtil.addPaths("aaa/;JS","bbb"),"aaa/bbb;JS");
assertEquals("aaa;JS+/", URIUtil.addPaths("aaa/;JS","/"),"aaa/;JS");
assertEquals("aaa;JS+/bbb", URIUtil.addPaths("aaa/;JS","/bbb"),"aaa/bbb;JS");
assertEquals("aaa;JS+bbb", URIUtil.addPaths("aaa/;JS","bbb"),"aaa/;JS/bbb");
assertEquals("aaa;JS+/", URIUtil.addPaths("aaa/;JS","/"),"aaa/;JS/");
assertEquals("aaa;JS+/bbb", URIUtil.addPaths("aaa/;JS","/bbb"),"aaa/;JS/bbb");
assertEquals("?A=1+null", URIUtil.addPaths("?A=1",null),"?A=1");
assertEquals("?A=1+", URIUtil.addPaths("?A=1",""),"?A=1");
assertEquals("?A=1+bbb", URIUtil.addPaths("?A=1","bbb"),"bbb?A=1");
assertEquals("?A=1+/", URIUtil.addPaths("?A=1","/"),"/?A=1");
assertEquals("?A=1+/bbb", URIUtil.addPaths("?A=1","/bbb"),"/bbb?A=1");
assertEquals("?A=1+bbb", URIUtil.addPaths("?A=1","bbb"),"?A=1/bbb");
assertEquals("?A=1+/", URIUtil.addPaths("?A=1","/"),"?A=1/");
assertEquals("?A=1+/bbb", URIUtil.addPaths("?A=1","/bbb"),"?A=1/bbb");
assertEquals("aaa?A=1+null", URIUtil.addPaths("aaa?A=1",null),"aaa?A=1");
assertEquals("aaa?A=1+", URIUtil.addPaths("aaa?A=1",""),"aaa?A=1");
assertEquals("aaa?A=1+bbb", URIUtil.addPaths("aaa?A=1","bbb"),"aaa/bbb?A=1");
assertEquals("aaa?A=1+/", URIUtil.addPaths("aaa?A=1","/"),"aaa/?A=1");
assertEquals("aaa?A=1+/bbb", URIUtil.addPaths("aaa?A=1","/bbb"),"aaa/bbb?A=1");
assertEquals("aaa?A=1+bbb", URIUtil.addPaths("aaa?A=1","bbb"),"aaa?A=1/bbb");
assertEquals("aaa?A=1+/", URIUtil.addPaths("aaa?A=1","/"),"aaa?A=1/");
assertEquals("aaa?A=1+/bbb", URIUtil.addPaths("aaa?A=1","/bbb"),"aaa?A=1/bbb");
assertEquals("aaa?A=1+null", URIUtil.addPaths("aaa/?A=1",null),"aaa/?A=1");
assertEquals("aaa?A=1+", URIUtil.addPaths("aaa/?A=1",""),"aaa/?A=1");
assertEquals("aaa?A=1+bbb", URIUtil.addPaths("aaa/?A=1","bbb"),"aaa/bbb?A=1");
assertEquals("aaa?A=1+/", URIUtil.addPaths("aaa/?A=1","/"),"aaa/?A=1");
assertEquals("aaa?A=1+/bbb", URIUtil.addPaths("aaa/?A=1","/bbb"),"aaa/bbb?A=1");
assertEquals(";JS?A=1+null", URIUtil.addPaths(";JS?A=1",null),";JS?A=1");
assertEquals(";JS?A=1+", URIUtil.addPaths(";JS?A=1",""),";JS?A=1");
assertEquals(";JS?A=1+bbb", URIUtil.addPaths(";JS?A=1","bbb"),"bbb;JS?A=1");
assertEquals(";JS?A=1+/", URIUtil.addPaths(";JS?A=1","/"),"/;JS?A=1");
assertEquals(";JS?A=1+/bbb", URIUtil.addPaths(";JS?A=1","/bbb"),"/bbb;JS?A=1");
assertEquals("aaa;JS?A=1+null", URIUtil.addPaths("aaa;JS?A=1",null),"aaa;JS?A=1");
assertEquals("aaa;JS?A=1+", URIUtil.addPaths("aaa;JS?A=1",""),"aaa;JS?A=1");
assertEquals("aaa;JS?A=1+bbb", URIUtil.addPaths("aaa;JS?A=1","bbb"),"aaa/bbb;JS?A=1");
assertEquals("aaa;JS?A=1+/", URIUtil.addPaths("aaa;JS?A=1","/"),"aaa/;JS?A=1");
assertEquals("aaa;JS?A=1+/bbb", URIUtil.addPaths("aaa;JS?A=1","/bbb"),"aaa/bbb;JS?A=1");
assertEquals("aaa;JS?A=1+null", URIUtil.addPaths("aaa/;JS?A=1",null),"aaa/;JS?A=1");
assertEquals("aaa;JS?A=1+", URIUtil.addPaths("aaa/;JS?A=1",""),"aaa/;JS?A=1");
assertEquals("aaa;JS?A=1+bbb", URIUtil.addPaths("aaa/;JS?A=1","bbb"),"aaa/bbb;JS?A=1");
assertEquals("aaa;JS?A=1+/", URIUtil.addPaths("aaa/;JS?A=1","/"),"aaa/;JS?A=1");
assertEquals("aaa;JS?A=1+/bbb", URIUtil.addPaths("aaa/;JS?A=1","/bbb"),"aaa/bbb;JS?A=1");
assertEquals("aaa?A=1+bbb", URIUtil.addPaths("aaa/?A=1","bbb"),"aaa/?A=1/bbb");
assertEquals("aaa?A=1+/", URIUtil.addPaths("aaa/?A=1","/"),"aaa/?A=1/");
assertEquals("aaa?A=1+/bbb", URIUtil.addPaths("aaa/?A=1","/bbb"),"aaa/?A=1/bbb");
}