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:
commit
6793e7e985
|
@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory;
|
|||
class AsyncContentProducer implements ContentProducer
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AsyncContentProducer.class);
|
||||
private static final HttpInput.ErrorContent RECYCLED_ERROR_CONTENT = new HttpInput.ErrorContent(new IllegalStateException("ContentProducer has been recycled"));
|
||||
private static final Throwable UNCONSUMED_CONTENT_EXCEPTION = new IOException("Unconsumed content")
|
||||
{
|
||||
@Override
|
||||
|
@ -66,9 +67,30 @@ class AsyncContentProducer implements ContentProducer
|
|||
assertLocked();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("recycling {}", this);
|
||||
|
||||
// Make sure that the content has been fully consumed before destroying the interceptor and also make sure
|
||||
// that asking this instance for content between recycle and reopen will only produce error'ed content.
|
||||
if (_rawContent == null)
|
||||
_rawContent = RECYCLED_ERROR_CONTENT;
|
||||
else if (!_rawContent.isSpecial())
|
||||
throw new IllegalStateException("ContentProducer with unconsumed content cannot be recycled");
|
||||
|
||||
if (_transformedContent == null)
|
||||
_transformedContent = RECYCLED_ERROR_CONTENT;
|
||||
else if (!_transformedContent.isSpecial())
|
||||
throw new IllegalStateException("ContentProducer with unconsumed content cannot be recycled");
|
||||
|
||||
if (_interceptor instanceof Destroyable)
|
||||
((Destroyable)_interceptor).destroy();
|
||||
_interceptor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reopen()
|
||||
{
|
||||
assertLocked();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("reopening {}", this);
|
||||
_rawContent = null;
|
||||
_transformedContent = null;
|
||||
_error = false;
|
||||
|
|
|
@ -46,6 +46,14 @@ class BlockingContentProducer implements ContentProducer
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("recycling {}", this);
|
||||
_asyncContentProducer.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reopen()
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("reopening {}", this);
|
||||
_asyncContentProducer.reopen();
|
||||
_semaphore.drainPermits();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import org.eclipse.jetty.util.component.Destroyable;
|
||||
import org.eclipse.jetty.util.thread.AutoLock;
|
||||
|
||||
/**
|
||||
|
@ -27,17 +28,24 @@ import org.eclipse.jetty.util.thread.AutoLock;
|
|||
public interface ContentProducer
|
||||
{
|
||||
/**
|
||||
* Lock this instance. The lock must be held before any method of this instance's
|
||||
* method be called, and must be manually released afterward.
|
||||
* Lock this instance. The lock must be held before any of this instance's
|
||||
* method can be called, and must be released afterward.
|
||||
* @return the lock that is guarding this instance.
|
||||
*/
|
||||
AutoLock lock();
|
||||
|
||||
/**
|
||||
* Reset all internal state and clear any held resources.
|
||||
* Clear the interceptor and call {@link Destroyable#destroy()} on it if it implements {@link Destroyable}.
|
||||
* A recycled {@link ContentProducer} will only produce special content with a non-null error until
|
||||
* {@link #reopen()} is called.
|
||||
*/
|
||||
void recycle();
|
||||
|
||||
/**
|
||||
* Reset all internal state, making this is instance logically equivalent to a freshly allocated one.
|
||||
*/
|
||||
void reopen();
|
||||
|
||||
/**
|
||||
* Fail all content currently available in this {@link ContentProducer} instance
|
||||
* as well as in the underlying {@link HttpChannel}.
|
||||
|
|
|
@ -55,8 +55,12 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
|
||||
public void recycle()
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("recycle {}", this);
|
||||
try (AutoLock lock = _contentProducer.lock())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("recycle {}", this);
|
||||
_blockingContentProducer.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
public void reopen()
|
||||
|
@ -65,7 +69,7 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("reopen {}", this);
|
||||
_blockingContentProducer.recycle();
|
||||
_blockingContentProducer.reopen();
|
||||
_contentProducer = _blockingContentProducer;
|
||||
_consumedEof = false;
|
||||
_readListener = null;
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.nio.charset.Charset;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Fast String Utilities.
|
||||
|
@ -774,6 +775,47 @@ public class StringUtil
|
|||
return true;
|
||||
}
|
||||
|
||||
public static byte[] fromHexString(String s)
|
||||
{
|
||||
if (s.length() % 2 != 0)
|
||||
throw new IllegalArgumentException(s);
|
||||
byte[] array = new byte[s.length() / 2];
|
||||
for (int i = 0; i < array.length; i++)
|
||||
{
|
||||
int b = Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16);
|
||||
array[i] = (byte)(0xff & b);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public static String toHexString(byte b)
|
||||
{
|
||||
return toHexString(new byte[]{b}, 0, 1);
|
||||
}
|
||||
|
||||
public static String toHexString(byte[] b)
|
||||
{
|
||||
return toHexString(Objects.requireNonNull(b, "ByteBuffer cannot be null"), 0, b.length);
|
||||
}
|
||||
|
||||
public static String toHexString(byte[] b, int offset, int length)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i = offset; i < offset + length; i++)
|
||||
{
|
||||
int bi = 0xff & b[i];
|
||||
int c = '0' + (bi / 16) % 16;
|
||||
if (c > '9')
|
||||
c = 'A' + (c - '0' - 10);
|
||||
buf.append((char)c);
|
||||
c = '0' + bi % 16;
|
||||
if (c > '9')
|
||||
c = 'a' + (c - '0' - 10);
|
||||
buf.append((char)c);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String printable(String name)
|
||||
{
|
||||
if (name == null)
|
||||
|
|
|
@ -400,6 +400,10 @@ public class TypeUtil
|
|||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link StringUtil#fromHexString(String)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static byte[] parseBytes(String s, int base)
|
||||
{
|
||||
byte[] bytes = new byte[s.length() / 2];
|
||||
|
@ -507,45 +511,40 @@ public class TypeUtil
|
|||
toHex((int)value, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link StringUtil#toHexString(byte)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static String toHexString(byte b)
|
||||
{
|
||||
return toHexString(new byte[]{b}, 0, 1);
|
||||
return StringUtil.toHexString(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link StringUtil#toHexString(byte[])} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static String toHexString(byte[] b)
|
||||
{
|
||||
return toHexString(b, 0, b.length);
|
||||
return StringUtil.toHexString(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link StringUtil#toHexString(byte[], int, int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static String toHexString(byte[] b, int offset, int length)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i = offset; i < offset + length; i++)
|
||||
{
|
||||
int bi = 0xff & b[i];
|
||||
int c = '0' + (bi / 16) % 16;
|
||||
if (c > '9')
|
||||
c = 'A' + (c - '0' - 10);
|
||||
buf.append((char)c);
|
||||
c = '0' + bi % 16;
|
||||
if (c > '9')
|
||||
c = 'a' + (c - '0' - 10);
|
||||
buf.append((char)c);
|
||||
}
|
||||
return buf.toString();
|
||||
return StringUtil.toHexString(b, offset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link StringUtil#fromHexString(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static byte[] fromHexString(String s)
|
||||
{
|
||||
if (s.length() % 2 != 0)
|
||||
throw new IllegalArgumentException(s);
|
||||
byte[] array = new byte[s.length() / 2];
|
||||
for (int i = 0; i < array.length; i++)
|
||||
{
|
||||
int b = Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16);
|
||||
array[i] = (byte)(0xff & b);
|
||||
}
|
||||
return array;
|
||||
return StringUtil.fromHexString(s);
|
||||
}
|
||||
|
||||
public static void dump(Class<?> c)
|
||||
|
|
|
@ -19,12 +19,9 @@ import java.security.MessageDigest;
|
|||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.thread.AutoLock;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Credentials. The Credential class represents an abstract mechanism for checking authentication credentials. A credential instance either represents a secret,
|
||||
|
@ -39,10 +36,12 @@ import org.slf4j.LoggerFactory;
|
|||
*/
|
||||
public abstract class Credential implements Serializable
|
||||
{
|
||||
// NOTE: DO NOT INTRODUCE LOGGING TO THIS CLASS
|
||||
private static final long serialVersionUID = -7760551052768181572L;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Credential.class);
|
||||
private static final List<CredentialProvider> CREDENTIAL_PROVIDERS = TypeUtil.serviceProviderStream(ServiceLoader.load(CredentialProvider.class))
|
||||
.flatMap(p -> Stream.of(p.get()))
|
||||
// Intentionally NOT using TypeUtil.serviceProviderStream
|
||||
// as that introduces a Logger requirement that command line Password cannot use.
|
||||
private static final List<CredentialProvider> CREDENTIAL_PROVIDERS = ServiceLoader.load(CredentialProvider.class).stream()
|
||||
.map(ServiceLoader.Provider::get)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
/**
|
||||
|
@ -154,8 +153,7 @@ public abstract class Credential implements Serializable
|
|||
{
|
||||
if (credentials instanceof char[])
|
||||
credentials = new String((char[])credentials);
|
||||
if (!(credentials instanceof String) && !(credentials instanceof Password))
|
||||
LOG.warn("Can't check {} against CRYPT", credentials.getClass());
|
||||
|
||||
return stringEquals(_cooked, UnixCrypt.crypt(credentials.toString(), _cooked));
|
||||
}
|
||||
|
||||
|
@ -189,7 +187,7 @@ public abstract class Credential implements Serializable
|
|||
MD5(String digest)
|
||||
{
|
||||
digest = digest.startsWith(__TYPE) ? digest.substring(__TYPE.length()) : digest;
|
||||
_digest = TypeUtil.parseBytes(digest, 16);
|
||||
_digest = StringUtil.fromHexString(digest);
|
||||
}
|
||||
|
||||
public byte[] getDigest()
|
||||
|
@ -229,13 +227,12 @@ public abstract class Credential implements Serializable
|
|||
}
|
||||
else
|
||||
{
|
||||
LOG.warn("Can't check {} against MD5", credentials.getClass());
|
||||
// Not a MD5 or Credential class
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Failed message digest", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -248,6 +245,9 @@ public abstract class Credential implements Serializable
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used only by Command Line Password utility
|
||||
*/
|
||||
public static String digest(String password)
|
||||
{
|
||||
try
|
||||
|
@ -263,7 +263,8 @@ public abstract class Credential implements Serializable
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Unable to access MD5 message digest", e);
|
||||
System.err.println("Unable to access MD5 message digest");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -273,11 +274,12 @@ public abstract class Credential implements Serializable
|
|||
digest = __md.digest();
|
||||
}
|
||||
|
||||
return __TYPE + TypeUtil.toString(digest, 16);
|
||||
return __TYPE + StringUtil.toHexString(digest);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Message Digest failure", e);
|
||||
System.err.println("Message Digest Failure");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,6 @@ import java.io.IOException;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Password utility class.
|
||||
*
|
||||
|
@ -47,8 +44,7 @@ import org.slf4j.LoggerFactory;
|
|||
*/
|
||||
public class Password extends Credential
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Password.class);
|
||||
|
||||
// NOTE: DO NOT INTRODUCE LOGGING TO THIS CLASS
|
||||
private static final long serialVersionUID = 5062906681431569445L;
|
||||
|
||||
public static final String __OBFUSCATE = "OBF:";
|
||||
|
@ -224,7 +220,9 @@ public class Password extends Credential
|
|||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.warn("EXCEPTION", e);
|
||||
// only seen with command line input style
|
||||
System.err.println("ERROR: Bad/Invalid password.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (passwd == null || passwd.length() == 0)
|
||||
passwd = promptDft;
|
||||
|
@ -247,5 +245,6 @@ public class Password extends Credential
|
|||
System.err.println(Credential.MD5.digest(p));
|
||||
if (arg.length == 2)
|
||||
System.err.println(Credential.Crypt.crypt(arg[0], pw.toString()));
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,10 @@ import static org.hamcrest.Matchers.arrayContaining;
|
|||
import static org.hamcrest.Matchers.emptyArray;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
// @checkstyle-disable-check : AvoidEscapedUnicodeCharactersCheck
|
||||
|
@ -266,4 +268,34 @@ public class StringUtilTest
|
|||
|
||||
assertThat(StringUtil.csvSplit("\"aaa\", \" b,\\\"\",\"\""), arrayContaining("aaa", " b,\"", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromHexStringGood()
|
||||
{
|
||||
assertArrayEquals(new byte[]{0x12, 0x34, 0x56, 0x78, (byte)0x9A}, StringUtil.fromHexString("123456789A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromHexStringBad()
|
||||
{
|
||||
assertThrows(NumberFormatException.class, () -> StringUtil.fromHexString("Hello World "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToHexStringGood()
|
||||
{
|
||||
assertThat(StringUtil.toHexString(new byte[]{0x12, 0x34, 0x56, 0x78, (byte)0x9A}), is("123456789a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToHexStringNull()
|
||||
{
|
||||
assertThrows(NullPointerException.class, () -> StringUtil.toHexString(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToHexStringEmpty()
|
||||
{
|
||||
assertThat(StringUtil.toHexString(new byte[0]), is(""));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ package org.eclipse.jetty.util.security;
|
|||
import org.eclipse.jetty.util.security.Credential.Crypt;
|
||||
import org.eclipse.jetty.util.security.Credential.MD5;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -99,4 +101,17 @@ public class CredentialTest
|
|||
assertFalse(Credential.byteEquals("".getBytes(), "fooo".getBytes()));
|
||||
assertTrue(Credential.byteEquals("".getBytes(), "".getBytes()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"OBF:1v2j1uum1xtv1zej1zer1xtn1uvk1v1v",
|
||||
"MD5:5f4dcc3b5aa765d61d8327deb882cf99",
|
||||
"CRYPT:usjRS48E8ZADM"
|
||||
})
|
||||
public void testGetCredential(String encoded)
|
||||
{
|
||||
Credential credential = Credential.getCredential(encoded);
|
||||
assertTrue(credential.check(Credential.getCredential("password")));
|
||||
assertTrue(credential.check("password"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,22 @@
|
|||
|
||||
package org.eclipse.jetty.util.security;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class PasswordTest
|
||||
{
|
||||
|
@ -44,4 +57,40 @@ public class PasswordTest
|
|||
String obfuscate = Password.obfuscate(password);
|
||||
assertEquals(password, Password.deobfuscate(obfuscate));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommandLineUsage() throws IOException, InterruptedException
|
||||
{
|
||||
ProcessBuilder passwordBuilder = new ProcessBuilder()
|
||||
.directory(MavenTestingUtils.getTargetDir())
|
||||
.command("java",
|
||||
"-cp", MavenTestingUtils.getTargetPath("classes").toString(),
|
||||
Password.class.getName(),
|
||||
"user", "password")
|
||||
.redirectErrorStream(true);
|
||||
|
||||
Process passwordProcess = passwordBuilder.start();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(passwordProcess.getInputStream())))
|
||||
{
|
||||
String output = reader.lines().collect(Collectors.joining(System.lineSeparator()));
|
||||
if (passwordProcess.waitFor(5, TimeUnit.SECONDS))
|
||||
{
|
||||
int exitCode = passwordProcess.exitValue();
|
||||
assertThat("Non-error exit code: " + output, exitCode, is(0));
|
||||
assertThat("Output", output, not(containsString("Exception")));
|
||||
assertThat("Output", output, allOf(
|
||||
containsString("password"),
|
||||
containsString("OBF:"),
|
||||
containsString("MD5:"),
|
||||
containsString("CRYPT:")
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println(output);
|
||||
passwordProcess.destroy();
|
||||
fail("Process didn't exit properly (was forcibly destroyed)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
4
pom.xml
4
pom.xml
|
@ -40,13 +40,13 @@
|
|||
<felix.version>7.0.3</felix.version>
|
||||
<findbugs.jsr305.version>3.0.2</findbugs.jsr305.version>
|
||||
<google.errorprone.version>2.10.0</google.errorprone.version>
|
||||
<grpc.version>1.42.1</grpc.version>
|
||||
<grpc.version>1.43.0</grpc.version>
|
||||
<gson.version>2.8.9</gson.version>
|
||||
<guava.version>31.0.1-jre</guava.version>
|
||||
<guice.version>5.0.1</guice.version>
|
||||
<hamcrest.version>2.2</hamcrest.version>
|
||||
<hawtio.version>2.14.2</hawtio.version>
|
||||
<hazelcast.version>4.2.2</hazelcast.version>
|
||||
<hazelcast.version>4.2.3</hazelcast.version>
|
||||
<infinispan.version>11.0.11.Final</infinispan.version>
|
||||
<infinispan.protostream.version>4.3.4.Final</infinispan.protostream.version>
|
||||
<jackson-databind.version>2.13.0</jackson-databind.version>
|
||||
|
|
Loading…
Reference in New Issue