Issue #4000 - PathResource alias detection work around alt UTF-8 style
+ OSX File is `swedish-å.txt` + OSX has NFD form UTF-8 characters. `swedish-a%CC%8A.txt` + HTTP uses normal form UTF-8. `swedish-%C3%A5.txt` + A HTTP GET request should work against the resource being requested, regardless of UTF-8 style used. Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
parent
407b564320
commit
ef3f696a11
|
@ -18,6 +18,20 @@
|
|||
|
||||
package org.eclipse.jetty.servlet;
|
||||
|
||||
import static org.eclipse.jetty.http.HttpFieldsMatchers.containsHeader;
|
||||
import static org.eclipse.jetty.http.HttpFieldsMatchers.containsHeaderValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
@ -35,6 +49,7 @@ import java.util.function.Consumer;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
|
@ -73,20 +88,6 @@ import org.junit.jupiter.params.provider.Arguments;
|
|||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.eclipse.jetty.http.HttpFieldsMatchers.containsHeader;
|
||||
import static org.eclipse.jetty.http.HttpFieldsMatchers.containsHeaderValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@ExtendWith(WorkDirExtension.class)
|
||||
public class DefaultServletTest
|
||||
{
|
||||
|
@ -2009,6 +2010,37 @@ public class DefaultServletTest
|
|||
response = HttpTester.parseResponse(rawResponse);
|
||||
assertThat(response.toString(), response.getStatus(), is(HttpStatus.PRECONDITION_FAILED_412));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUnicodeFile() throws Exception
|
||||
{
|
||||
FS.ensureEmpty(docRoot);
|
||||
System.err.printf("docRoot is %s%n", docRoot);
|
||||
|
||||
context.addServlet(DefaultServlet.class, "/");
|
||||
|
||||
createFile(docRoot.resolve("swedish-å.txt"), "hi a-with-circle");
|
||||
createFile(docRoot.resolve("swedish-ä.txt"), "hi a-with-two-dots");
|
||||
createFile(docRoot.resolve("swedish-ö.txt"), "hi o-with-two-dots");
|
||||
|
||||
String rawResponse;
|
||||
HttpTester.Response response;
|
||||
|
||||
rawResponse = connector.getResponse("GET /context/swedish-%C3%A5.txt HTTP/1.1\r\nHost:test\r\nConnection:close\r\n\r\n");
|
||||
response = HttpTester.parseResponse(rawResponse);
|
||||
assertThat(response.getStatus(), is(HttpStatus.OK_200));
|
||||
assertThat(response.getContent(), is("hi a-with-circle"));
|
||||
|
||||
rawResponse = connector.getResponse("GET /context/swedish-%C3%A4.txt HTTP/1.1\r\nHost:test\r\nConnection:close\r\n\r\n");
|
||||
response = HttpTester.parseResponse(rawResponse);
|
||||
assertThat(response.getStatus(), is(HttpStatus.OK_200));
|
||||
assertThat(response.getContent(), is("hi a-with-two-dots"));
|
||||
|
||||
rawResponse = connector.getResponse("GET /context/swedish-%C3%B6.txt HTTP/1.1\r\nHost:test\r\nConnection:close\r\n\r\n");
|
||||
response = HttpTester.parseResponse(rawResponse);
|
||||
assertThat(response.getStatus(), is(HttpStatus.OK_200));
|
||||
assertThat(response.getContent(), is("hi o-with-two-dots"));
|
||||
}
|
||||
|
||||
public static class OutputFilter implements Filter
|
||||
{
|
||||
|
|
|
@ -80,7 +80,15 @@ public class PathResource extends Resource
|
|||
{
|
||||
try
|
||||
{
|
||||
return Paths.get(uri).toRealPath(FOLLOW_LINKS);
|
||||
Path followed = Paths.get(uri).toRealPath(FOLLOW_LINKS);
|
||||
if (!isSame(path, followed))
|
||||
{
|
||||
return followed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
|
@ -137,23 +145,11 @@ public class PathResource extends Resource
|
|||
* We also cannot rely on a.compareTo(b) as this is roughly equivalent
|
||||
* in implementation to a.equals(b)
|
||||
*/
|
||||
|
||||
int absCount = abs.getNameCount();
|
||||
int realCount = real.getNameCount();
|
||||
if (absCount != realCount)
|
||||
|
||||
if (!isSame(abs, real))
|
||||
{
|
||||
// different number of segments
|
||||
return real;
|
||||
}
|
||||
|
||||
// compare each segment of path, backwards
|
||||
for (int i = realCount - 1; i >= 0; i--)
|
||||
{
|
||||
if (!abs.getName(i).toString().equals(real.getName(i).toString()))
|
||||
{
|
||||
return real;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
|
@ -166,6 +162,28 @@ public class PathResource extends Resource
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isSame(Path pathA, Path pathB)
|
||||
{
|
||||
int aCount = pathA.getNameCount();
|
||||
int bCount = pathB.getNameCount();
|
||||
if (aCount != bCount)
|
||||
{
|
||||
// different number of segments
|
||||
return false;
|
||||
}
|
||||
|
||||
// compare each segment of path, backwards
|
||||
for (int i = bCount - 1; i >= 0; i--)
|
||||
{
|
||||
if (!pathA.getName(i).toString().equals(pathB.getName(i).toString()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new PathResource from a File object.
|
||||
|
|
|
@ -342,9 +342,13 @@ public class FileSystemResourceTest
|
|||
Resource refA2 = base.addPath("swedish-ä.txt");
|
||||
Resource refO1 = base.addPath("swedish-ö.txt");
|
||||
|
||||
assertThat("Ref A1", refA1.exists(), is(true));
|
||||
assertThat("Ref A2", refA2.exists(), is(true));
|
||||
assertThat("Ref O1", refO1.exists(), is(true));
|
||||
assertThat("Ref A1 exists", refA1.exists(), is(true));
|
||||
assertThat("Ref A2 exists", refA2.exists(), is(true));
|
||||
assertThat("Ref O1 exists", refO1.exists(), is(true));
|
||||
|
||||
assertThat("Ref A1 alias", refA1.isAlias(), is(false));
|
||||
assertThat("Ref A2 alias", refA2.isAlias(), is(false));
|
||||
assertThat("Ref O1 alias", refO1.isAlias(), is(false));
|
||||
|
||||
assertThat("Ref A1 contents", toString(refA1), is("hi a-with-circle"));
|
||||
assertThat("Ref A2 contents", toString(refA2), is("hi a-with-two-dots"));
|
||||
|
@ -1416,11 +1420,19 @@ public class FileSystemResourceTest
|
|||
|
||||
Resource r = base.addPath("//foo.txt");
|
||||
assertThat("getURI()", r.getURI().toASCIIString(), containsString("//foo.txt"));
|
||||
|
||||
assertThat("isAlias()", r.isAlias(), is(true));
|
||||
assertThat("getAlias()", r.getAlias(), notNullValue());
|
||||
assertThat("getAlias()", r.getAlias().toASCIIString(), containsString("/foo.txt"));
|
||||
assertThat("Exists: " + r, r.exists(), is(true));
|
||||
|
||||
if (PathResource.class.isAssignableFrom(resourceClass))
|
||||
{
|
||||
assertThat("isAlias()", r.isAlias(), is(false));
|
||||
assertThat("getAlias()", r.getAlias(), nullValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
assertThat("isAlias()", r.isAlias(), is(true));
|
||||
assertThat("getAlias()", r.getAlias(), notNullValue());
|
||||
assertThat("getAlias()", r.getAlias().toASCIIString(), containsString("/foo.txt"));
|
||||
}
|
||||
}
|
||||
catch (InvalidPathException e)
|
||||
{
|
||||
|
@ -1449,10 +1461,19 @@ public class FileSystemResourceTest
|
|||
|
||||
Resource r = base.addPath("aa//foo.txt");
|
||||
assertThat("getURI()", r.getURI().toASCIIString(), containsString("aa//foo.txt"));
|
||||
|
||||
assertThat("isAlias()", r.isAlias(), is(true));
|
||||
assertThat("getAlias()", r.getAlias(), notNullValue());
|
||||
assertThat("getAlias()", r.getAlias().toASCIIString(), containsString("aa/foo.txt"));
|
||||
|
||||
if (PathResource.class.isAssignableFrom(resourceClass))
|
||||
{
|
||||
assertThat("isAlias()", r.isAlias(), is(false));
|
||||
assertThat("getAlias()", r.getAlias(), nullValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
assertThat("isAlias()", r.isAlias(), is(true));
|
||||
assertThat("getAlias()", r.getAlias(), notNullValue());
|
||||
assertThat("getAlias()", r.getAlias().toASCIIString(), containsString("aa/foo.txt"));
|
||||
}
|
||||
|
||||
assertThat("Exists: " + r, r.exists(), is(true));
|
||||
}
|
||||
catch (InvalidPathException e)
|
||||
|
|
Loading…
Reference in New Issue