Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-11.0.x

Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
Ludovic Orban 2021-07-29 11:17:09 +02:00
commit aeeed200dc
3 changed files with 152 additions and 111 deletions

View File

@ -3,11 +3,13 @@ aif=audio/x-aiff
aifc=audio/x-aiff
aiff=audio/x-aiff
apk=application/vnd.android.package-archive
apng=image/apng
asc=text/plain
asf=video/x.ms.asf
asx=video/x.ms.asx
au=audio/basic
avi=video/x-msvideo
avif=image/avif
bcpio=application/x-bcpio
bin=application/octet-stream
bmp=image/bmp
@ -170,6 +172,7 @@ vxml=application/voicexml+xml
wasm=application/wasm
wav=audio/x-wav
wbmp=image/vnd.wap.wbmp
webp=image/webp
wml=text/vnd.wap.wml
wmlc=application/vnd.wap.wmlc
wmls=text/vnd.wap.wmlscript

View File

@ -13,47 +13,48 @@
package org.eclipse.jetty.http;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
public class MimeTypesTest
{
@Test
public void testGetMimeByExtensionGzip()
public static Stream<Arguments> mimeTypesByExtensionCases()
{
assertMimeTypeByExtension("application/gzip", "test.gz");
return Stream.of(
Arguments.of("test.gz", "application/gzip"),
Arguments.of("foo.webp", "image/webp"),
Arguments.of("zed.avif", "image/avif"),
// make sure that filename case isn't an issue
Arguments.of("test.png", "image/png"),
Arguments.of("TEST.PNG", "image/png"),
Arguments.of("Test.Png", "image/png"),
Arguments.of("test.txt", "text/plain"),
Arguments.of("TEST.TXT", "text/plain"),
// Make sure that multiple dots don't interfere
Arguments.of("org.eclipse.jetty.Logo.png", "image/png"),
// Make sure that a deep path doesn't interfere
Arguments.of("org/eclipse/jetty/Logo.png", "image/png"),
// Make sure that path that looks like a filename doesn't interfere
Arguments.of("org/eclipse.jpg/jetty/Logo.png", "image/png")
);
}
@Test
public void testGetMimeByExtensionPng()
@ParameterizedTest
@MethodSource("mimeTypesByExtensionCases")
public void testMimeTypesByExtension(String filename, String expectedMimeType)
{
assertMimeTypeByExtension("image/png", "test.png");
assertMimeTypeByExtension("image/png", "TEST.PNG");
assertMimeTypeByExtension("image/png", "Test.Png");
}
@Test
public void testGetMimeByExtensionPngMultiDot()
{
assertMimeTypeByExtension("image/png", "org.eclipse.jetty.Logo.png");
}
@Test
public void testGetMimeByExtensionPngDeepPath()
{
assertMimeTypeByExtension("image/png", "/org/eclipse/jetty/Logo.png");
}
@Test
public void testGetMimeByExtensionText()
{
assertMimeTypeByExtension("text/plain", "test.txt");
assertMimeTypeByExtension("text/plain", "TEST.TXT");
MimeTypes mimetypes = new MimeTypes();
String contentType = mimetypes.getMimeByExtension(filename);
assertThat("MimeTypes.getMimeByExtension(\"" + filename + "\")",
contentType, is(expectedMimeType));
}
@Test
@ -64,60 +65,63 @@ public class MimeTypesTest
assertNull(contentType);
}
private void assertMimeTypeByExtension(String expectedMimeType, String filename)
public static Stream<Arguments> charsetFromContentTypeCases()
{
MimeTypes mimetypes = new MimeTypes();
String contentType = mimetypes.getMimeByExtension(filename);
String prefix = "MimeTypes.getMimeByExtension(" + filename + ")";
assertNotNull(contentType, prefix);
assertEquals(expectedMimeType, contentType, prefix);
return Stream.of(
Arguments.of("foo/bar;charset=abc;some=else", "abc"),
Arguments.of("foo/bar;charset=abc", "abc"),
Arguments.of("foo/bar ; charset = abc", "abc"),
Arguments.of("foo/bar ; charset = abc ; some=else", "abc"),
Arguments.of("foo/bar;other=param;charset=abc;some=else", "abc"),
Arguments.of("foo/bar;other=param;charset=abc", "abc"),
Arguments.of("foo/bar other = param ; charset = abc", "abc"),
Arguments.of("foo/bar other = param ; charset = abc ; some=else", "abc"),
Arguments.of("foo/bar other = param ; charset = abc", "abc"),
Arguments.of("foo/bar other = param ; charset = \"abc\" ; some=else", "abc"),
Arguments.of("foo/bar", null),
Arguments.of("foo/bar;charset=uTf8", "utf-8"),
Arguments.of("foo/bar;other=\"charset=abc\";charset=uTf8", "utf-8"),
Arguments.of("application/pdf; charset=UTF-8", "utf-8"),
Arguments.of("application/pdf;; charset=UTF-8", "utf-8"),
Arguments.of("application/pdf;;; charset=UTF-8", "utf-8"),
Arguments.of("application/pdf;;;; charset=UTF-8", "utf-8"),
Arguments.of("text/html;charset=utf-8", "utf-8")
);
}
private void assertCharsetFromContentType(String contentType, String expectedCharset)
@ParameterizedTest
@MethodSource("charsetFromContentTypeCases")
public void testCharsetFromContentType(String contentType, String expectedCharset)
{
assertThat("getCharsetFromContentType(\"" + contentType + "\")",
MimeTypes.getCharsetFromContentType(contentType), is(expectedCharset));
}
@Test
public void testCharsetFromContentType()
public static Stream<Arguments> contentTypeWithoutCharsetCases()
{
assertCharsetFromContentType("foo/bar;charset=abc;some=else", "abc");
assertCharsetFromContentType("foo/bar;charset=abc", "abc");
assertCharsetFromContentType("foo/bar ; charset = abc", "abc");
assertCharsetFromContentType("foo/bar ; charset = abc ; some=else", "abc");
assertCharsetFromContentType("foo/bar;other=param;charset=abc;some=else", "abc");
assertCharsetFromContentType("foo/bar;other=param;charset=abc", "abc");
assertCharsetFromContentType("foo/bar other = param ; charset = abc", "abc");
assertCharsetFromContentType("foo/bar other = param ; charset = abc ; some=else", "abc");
assertCharsetFromContentType("foo/bar other = param ; charset = abc", "abc");
assertCharsetFromContentType("foo/bar other = param ; charset = \"abc\" ; some=else", "abc");
assertCharsetFromContentType("foo/bar", null);
assertCharsetFromContentType("foo/bar;charset=uTf8", "utf-8");
assertCharsetFromContentType("foo/bar;other=\"charset=abc\";charset=uTf8", "utf-8");
assertCharsetFromContentType("application/pdf; charset=UTF-8", "utf-8");
assertCharsetFromContentType("application/pdf;; charset=UTF-8", "utf-8");
assertCharsetFromContentType("application/pdf;;; charset=UTF-8", "utf-8");
assertCharsetFromContentType("application/pdf;;;; charset=UTF-8", "utf-8");
assertCharsetFromContentType("text/html;charset=utf-8", "utf-8");
return Stream.of(
Arguments.of("foo/bar;charset=abc;some=else", "foo/bar;some=else"),
Arguments.of("foo/bar;charset=abc", "foo/bar"),
Arguments.of("foo/bar ; charset = abc", "foo/bar"),
Arguments.of("foo/bar ; charset = abc ; some=else", "foo/bar;some=else"),
Arguments.of("foo/bar;other=param;charset=abc;some=else", "foo/bar;other=param;some=else"),
Arguments.of("foo/bar;other=param;charset=abc", "foo/bar;other=param"),
Arguments.of("foo/bar ; other = param ; charset = abc", "foo/bar ; other = param"),
Arguments.of("foo/bar ; other = param ; charset = abc ; some=else", "foo/bar ; other = param;some=else"),
Arguments.of("foo/bar ; other = param ; charset = abc", "foo/bar ; other = param"),
Arguments.of("foo/bar ; other = param ; charset = \"abc\" ; some=else", "foo/bar ; other = param;some=else"),
Arguments.of("foo/bar", "foo/bar"),
Arguments.of("foo/bar;charset=uTf8", "foo/bar"),
Arguments.of("foo/bar;other=\"charset=abc\";charset=uTf8", "foo/bar;other=\"charset=abc\""),
Arguments.of("text/html;charset=utf-8", "text/html")
);
}
@Test
public void testContentTypeWithoutCharset()
@ParameterizedTest
@MethodSource("contentTypeWithoutCharsetCases")
public void testContentTypeWithoutCharset(String contentTypeWithCharset, String expectedContentType)
{
assertEquals("foo/bar;some=else", MimeTypes.getContentTypeWithoutCharset("foo/bar;charset=abc;some=else"));
assertEquals("foo/bar", MimeTypes.getContentTypeWithoutCharset("foo/bar;charset=abc"));
assertEquals("foo/bar", MimeTypes.getContentTypeWithoutCharset("foo/bar ; charset = abc"));
assertEquals("foo/bar;some=else", MimeTypes.getContentTypeWithoutCharset("foo/bar ; charset = abc ; some=else"));
assertEquals("foo/bar;other=param;some=else", MimeTypes.getContentTypeWithoutCharset("foo/bar;other=param;charset=abc;some=else"));
assertEquals("foo/bar;other=param", MimeTypes.getContentTypeWithoutCharset("foo/bar;other=param;charset=abc"));
assertEquals("foo/bar ; other = param", MimeTypes.getContentTypeWithoutCharset("foo/bar ; other = param ; charset = abc"));
assertEquals("foo/bar ; other = param;some=else", MimeTypes.getContentTypeWithoutCharset("foo/bar ; other = param ; charset = abc ; some=else"));
assertEquals("foo/bar ; other = param", MimeTypes.getContentTypeWithoutCharset("foo/bar ; other = param ; charset = abc"));
assertEquals("foo/bar ; other = param;some=else", MimeTypes.getContentTypeWithoutCharset("foo/bar ; other = param ; charset = \"abc\" ; some=else"));
assertEquals("foo/bar", MimeTypes.getContentTypeWithoutCharset("foo/bar"));
assertEquals("foo/bar", MimeTypes.getContentTypeWithoutCharset("foo/bar;charset=uTf8"));
assertEquals("foo/bar;other=\"charset=abc\"", MimeTypes.getContentTypeWithoutCharset("foo/bar;other=\"charset=abc\";charset=uTf8"));
assertEquals("text/html", MimeTypes.getContentTypeWithoutCharset("text/html;charset=utf-8"));
assertThat("MimeTypes.getContentTypeWithoutCharset(\"" + contentTypeWithCharset + "\")",
MimeTypes.getContentTypeWithoutCharset(contentTypeWithCharset), is(expectedContentType));
}
}

View File

@ -13,23 +13,28 @@
package org.eclipse.jetty.util;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
import static org.eclipse.jetty.util.BlockingArrayQueueTest.Await.await;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@ -161,12 +166,12 @@ public class BlockingArrayQueueTest
}
@Test
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
public void testTake() throws Exception
{
final String[] data = new String[4];
final BlockingArrayQueue<String> queue = new BlockingArrayQueue<>();
CyclicBarrier barrier = new CyclicBarrier(2);
Thread thread = new Thread()
{
@ -177,7 +182,7 @@ public class BlockingArrayQueueTest
{
data[0] = queue.take();
data[1] = queue.take();
Thread.sleep(1000);
barrier.await(5, TimeUnit.SECONDS); // Wait until the main thread already called offer().
data[2] = queue.take();
data[3] = queue.poll(100, TimeUnit.MILLISECONDS);
}
@ -191,35 +196,36 @@ public class BlockingArrayQueueTest
thread.start();
Thread.sleep(1000);
// Wait until the spawned thread is blocked in queue.take().
await().atMost(5, TimeUnit.SECONDS).until(() -> thread.getState() == Thread.State.WAITING);
queue.offer("zero");
queue.offer("one");
queue.offer("two");
barrier.await(5, TimeUnit.SECONDS); // Notify the spawned thread that offer() was called.
thread.join();
assertEquals("zero", data[0]);
assertEquals("one", data[1]);
assertEquals("two", data[2]);
assertEquals(null, data[3]);
assertNull(data[3]);
}
@Test
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
public void testConcurrentAccess() throws Exception
{
final int THREADS = 50;
final int THREADS = 32;
final int LOOPS = 1000;
final BlockingArrayQueue<Integer> queue = new BlockingArrayQueue<>(1 + THREADS * LOOPS);
BlockingArrayQueue<Integer> queue = new BlockingArrayQueue<>(1 + THREADS * LOOPS);
final ConcurrentLinkedQueue<Integer> produced = new ConcurrentLinkedQueue<>();
final ConcurrentLinkedQueue<Integer> consumed = new ConcurrentLinkedQueue<>();
Set<Integer> produced = ConcurrentHashMap.newKeySet();
Set<Integer> consumed = ConcurrentHashMap.newKeySet();
final AtomicBoolean running = new AtomicBoolean(true);
AtomicBoolean consumersRunning = new AtomicBoolean(true);
// start consumers
final CyclicBarrier barrier0 = new CyclicBarrier(THREADS + 1);
CyclicBarrier consumersBarrier = new CyclicBarrier(THREADS + 1);
for (int i = 0; i < THREADS; i++)
{
new Thread()
@ -227,20 +233,18 @@ public class BlockingArrayQueueTest
@Override
public void run()
{
final Random random = new Random();
setPriority(getPriority() - 1);
try
{
while (running.get())
while (consumersRunning.get())
{
int r = 1 + random.nextInt(10);
int r = 1 + ThreadLocalRandom.current().nextInt(10);
if (r % 2 == 0)
{
Integer msg = queue.poll();
if (msg == null)
{
Thread.sleep(1 + random.nextInt(10));
Thread.sleep(ThreadLocalRandom.current().nextInt(2));
continue;
}
consumed.add(msg);
@ -261,7 +265,7 @@ public class BlockingArrayQueueTest
{
try
{
barrier0.await();
consumersBarrier.await();
}
catch (Exception e)
{
@ -273,7 +277,7 @@ public class BlockingArrayQueueTest
}
// start producers
final CyclicBarrier barrier1 = new CyclicBarrier(THREADS + 1);
CyclicBarrier producersBarrier = new CyclicBarrier(THREADS + 1);
for (int i = 0; i < THREADS; i++)
{
final int id = i;
@ -282,16 +286,15 @@ public class BlockingArrayQueueTest
@Override
public void run()
{
final Random random = new Random();
try
{
for (int j = 0; j < LOOPS; j++)
{
Integer msg = random.nextInt();
Integer msg = ThreadLocalRandom.current().nextInt();
produced.add(msg);
if (!queue.offer(msg))
throw new Exception(id + " FULL! " + queue.size());
Thread.sleep(1 + random.nextInt(10));
Thread.sleep(ThreadLocalRandom.current().nextInt(2));
}
}
catch (Exception e)
@ -302,7 +305,7 @@ public class BlockingArrayQueueTest
{
try
{
barrier1.await();
producersBarrier.await();
}
catch (Exception e)
{
@ -313,22 +316,22 @@ public class BlockingArrayQueueTest
}.start();
}
barrier1.await();
int size = queue.size();
int last = size - 1;
while (size > 0 && size != last)
producersBarrier.await();
AtomicInteger last = new AtomicInteger(queue.size() - 1);
await().atMost(5, TimeUnit.SECONDS).until(() ->
{
last = size;
Thread.sleep(500);
size = queue.size();
}
running.set(false);
barrier0.await();
int size = queue.size();
if (size == 0 && last.get() == size)
return true;
last.set(size);
return false;
});
HashSet<Integer> prodSet = new HashSet<>(produced);
HashSet<Integer> consSet = new HashSet<>(consumed);
consumersRunning.set(false);
consumersBarrier.await();
assertEquals(prodSet, consSet);
assertEquals(produced, consumed);
}
@Test
@ -525,4 +528,35 @@ public class BlockingArrayQueueTest
assertThat(queue.size(), Matchers.is(0));
assertThat(queue, Matchers.empty());
}
static class Await
{
private Duration duration;
public static Await await()
{
return new Await();
}
public Await atMost(long time, TimeUnit unit)
{
duration = Duration.ofMillis(unit.toMillis(time));
return this;
}
public void until(Callable<Boolean> condition) throws Exception
{
Objects.requireNonNull(duration);
long start = System.nanoTime();
while (true)
{
if (condition.call())
return;
if (duration.minus(Duration.ofNanos(System.nanoTime() - start)).isNegative())
throw new AssertionError("Duration expired");
Thread.sleep(10);
}
}
}
}