Merge pull request #468 from thesnowgoose/master

Improve canonicalPath method in URIUtil.java contemplating new test added on issue #466
This commit is contained in:
Greg Wilkins 2016-04-05 15:12:06 +10:00
commit eeb42338fc
1 changed files with 30 additions and 117 deletions

View File

@ -20,6 +20,8 @@ package org.eclipse.jetty.util;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Pattern;
import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
import org.eclipse.jetty.util.log.Log;
@ -49,6 +51,7 @@ public class URIUtil
public static final String HTTP_COLON="http:";
public static final String HTTPS="https";
public static final String HTTPS_COLON="https:";
private static final Pattern __PATH_SPLIT = Pattern.compile("(?<=\\/)");
// Use UTF-8 as per http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars
public static final Charset __CHARSET=StandardCharsets.UTF_8 ;
@ -503,130 +506,40 @@ public class URIUtil
*/
public static String canonicalPath(String path)
{
if (path==null || path.length()==0)
if (path == null || path.isEmpty() || !path.contains("."))
return path;
int end=path.length();
int start = path.lastIndexOf('/', end);
if(path.startsWith("/.."))
return null;
search:
while (end>0)
List<String> directories = new ArrayList<>();
Collections.addAll(directories, __PATH_SPLIT.split(path));
for(ListIterator<String> iterator = directories.listIterator(); iterator.hasNext();)
{
switch(end-start)
{
case 2: // possible single dot
if (path.charAt(start+1)!='.')
break;
break search;
case 3: // possible double dot
if (path.charAt(start+1)!='.' || path.charAt(start+2)!='.')
break;
break search;
switch (iterator.next()) {
case "./":
case ".":
if (iterator.hasNext() && directories.get(iterator.nextIndex()).equals("/"))
break;
iterator.remove();
break;
case "../":
case "..":
if(iterator.previousIndex() == 0)
return null;
iterator.remove();
if(iterator.previous().equals("/") && iterator.nextIndex() == 0)
return null;
iterator.remove();
break;
}
end=start;
start=path.lastIndexOf('/',end-1);
}
// If we have checked the entire string
if (start>=end)
return path;
StringBuilder buf = new StringBuilder(path);
int delStart=-1;
int delEnd=-1;
int skip=0;
while (end>0)
{
switch(end-start)
{
case 2: // possible single dot
if (buf.charAt(start+1)!='.')
{
if (skip>0 && --skip==0)
{
delStart=start>=0?start:0;
if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.')
delStart++;
}
break;
}
if(start<0 && buf.length()>2 && buf.charAt(1)=='/' && buf.charAt(2)=='/')
break;
if(delEnd<0)
delEnd=end;
delStart=start;
if (delStart<0 || delStart==0&&buf.charAt(delStart)=='/')
{
delStart++;
if (delEnd<buf.length() && buf.charAt(delEnd)=='/')
delEnd++;
break;
}
if (end==buf.length())
delStart++;
end=start--;
while (start>=0 && buf.charAt(start)!='/')
start--;
continue;
case 3: // possible double dot
if (buf.charAt(start+1)!='.' || buf.charAt(start+2)!='.')
{
if (skip>0 && --skip==0)
{ delStart=start>=0?start:0;
if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.')
delStart++;
}
break;
}
delStart=start;
if (delEnd<0)
delEnd=end;
skip++;
end=start--;
while (start>=0 && buf.charAt(start)!='/')
start--;
continue;
default:
if (skip>0 && --skip==0)
{
delStart=start>=0?start:0;
if(delEnd==buf.length() && buf.charAt(delEnd-1)=='.')
delStart++;
}
}
// Do the delete
if (skip<=0 && delStart>=0 && delEnd>=delStart)
{
buf.delete(delStart,delEnd);
delStart=delEnd=-1;
if (skip>0)
delEnd=end;
}
end=start--;
while (start>=0 && buf.charAt(start)!='/')
start--;
}
// Too many ..
if (skip>0)
return null;
// Do the delete
if (delEnd>=0)
buf.delete(delStart,delEnd);
return buf.toString();
return String.join("", directories);
}
/* ------------------------------------------------------------ */