diff --git a/src/main/java/io/nats/client/impl/NatsConnection.java b/src/main/java/io/nats/client/impl/NatsConnection.java index 8bb4a9599..8aaba3605 100644 --- a/src/main/java/io/nats/client/impl/NatsConnection.java +++ b/src/main/java/io/nats/client/impl/NatsConnection.java @@ -38,12 +38,10 @@ import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Predicate; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import static io.nats.client.support.NatsConstants.*; import static io.nats.client.support.NatsRequestCompletableFuture.CancelAction; -import static io.nats.client.support.Validator.validateNotNull; +import static io.nats.client.support.Validator.*; import static java.nio.charset.StandardCharsets.UTF_8; class NatsConnection implements Connection { @@ -854,18 +852,7 @@ private void checkIfNeedsHeaderSupport(Headers headers) { */ @Override public Subscription subscribe(String subject) { - - if (subject == null || subject.length() == 0) { - throw new IllegalArgumentException("Subject is required in subscribe"); - } - - Pattern pattern = Pattern.compile("\\s"); - Matcher matcher = pattern.matcher(subject); - - if (matcher.find()) { - throw new IllegalArgumentException("Subject cannot contain whitespace"); - } - + validateSubject(subject, true); return createSubscription(subject, null, null, null); } @@ -874,28 +861,8 @@ public Subscription subscribe(String subject) { */ @Override public Subscription subscribe(String subject, String queueName) { - - if (subject == null || subject.length() == 0) { - throw new IllegalArgumentException("Subject is required in subscribe"); - } - - Pattern pattern = Pattern.compile("\\s"); - Matcher smatcher = pattern.matcher(subject); - - if (smatcher.find()) { - throw new IllegalArgumentException("Subject cannot contain whitespace"); - } - - if (queueName == null || queueName.length() == 0) { - throw new IllegalArgumentException("QueueName is required in subscribe"); - } - - Matcher qmatcher = pattern.matcher(queueName); - - if (qmatcher.find()) { - throw new IllegalArgumentException("Queue names cannot contain whitespace"); - } - + validateSubject(subject, true); + validateQueueName(queueName, true); return createSubscription(subject, queueName, null, null); } diff --git a/src/main/java/io/nats/client/impl/NatsDispatcher.java b/src/main/java/io/nats/client/impl/NatsDispatcher.java index fd6d5fa38..e2dffff38 100644 --- a/src/main/java/io/nats/client/impl/NatsDispatcher.java +++ b/src/main/java/io/nats/client/impl/NatsDispatcher.java @@ -23,6 +23,8 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; +import static io.nats.client.support.Validator.*; + class NatsDispatcher extends NatsConsumer implements Dispatcher, Runnable { private MessageQueue incoming; @@ -187,53 +189,32 @@ void remove(NatsSubscription sub) { } public Dispatcher subscribe(String subject) { - if (subject == null || subject.length() == 0) { - throw new IllegalArgumentException("Subject is required in subscribe"); - } - + validateSubject(subject, true); this.subscribeImplCore(subject, null, null); return this; } - NatsSubscription subscribeReturningSubscription(String subject) { - if (subject == null || subject.length() == 0) { - throw new IllegalArgumentException("Subject is required in subscribe"); - } + NatsSubscription subscribeReturningSubscription(String subject) { + validateSubject(subject, true); return this.subscribeImplCore(subject, null, null); } public Subscription subscribe(String subject, MessageHandler handler) { - if (subject == null || subject.length() == 0) { - throw new IllegalArgumentException("Subject is required in subscribe"); - } - - if (handler == null) { - throw new IllegalArgumentException("MessageHandler is required in subscribe"); - } + validateSubject(subject, true); + required(handler, "Handler"); return this.subscribeImplCore(subject, null, handler); } public Dispatcher subscribe(String subject, String queueName) { - if (subject == null || subject.length() == 0) { - throw new IllegalArgumentException("Subject is required in subscribe"); - } - - if (queueName == null || queueName.length() == 0) { - throw new IllegalArgumentException("QueueName is required in subscribe"); - } + validateSubject(subject, true); + validateQueueName(queueName, true); this.subscribeImplCore(subject, queueName, null); return this; } public Subscription subscribe(String subject, String queueName, MessageHandler handler) { - if (subject == null || subject.length() == 0) { - throw new IllegalArgumentException("Subject is required in subscribe"); - } - - if (queueName == null || queueName.length() == 0) { - throw new IllegalArgumentException("QueueName is required in subscribe"); - } - + validateSubject(subject, true); + validateQueueName(queueName, true); if (handler == null) { throw new IllegalArgumentException("MessageHandler is required in subscribe"); } diff --git a/src/main/java/io/nats/client/impl/NatsJetStream.java b/src/main/java/io/nats/client/impl/NatsJetStream.java index e85e1adfa..ccc3c35cb 100644 --- a/src/main/java/io/nats/client/impl/NatsJetStream.java +++ b/src/main/java/io/nats/client/impl/NatsJetStream.java @@ -562,6 +562,7 @@ private String lookupStreamSubject(String stream) throws IOException, JetStreamA */ @Override public JetStreamSubscription subscribe(String subscribeSubject) throws IOException, JetStreamApiException { + subscribeSubject = validateSubject(subscribeSubject, true); return createSubscription(subscribeSubject, null, null, null, null, null, false); } @@ -570,6 +571,7 @@ public JetStreamSubscription subscribe(String subscribeSubject) throws IOExcepti */ @Override public JetStreamSubscription subscribe(String subscribeSubject, PushSubscribeOptions options) throws IOException, JetStreamApiException { + subscribeSubject = validateSubject(subscribeSubject, false); return createSubscription(subscribeSubject, options, null, null, null, null, false); } @@ -578,7 +580,8 @@ public JetStreamSubscription subscribe(String subscribeSubject, PushSubscribeOpt */ @Override public JetStreamSubscription subscribe(String subscribeSubject, String queue, PushSubscribeOptions options) throws IOException, JetStreamApiException { - queue = validateQueueName(emptyAsNull(queue), false); + subscribeSubject = validateSubject(subscribeSubject, false); + validateQueueName(queue, false); return createSubscription(subscribeSubject, options, null, queue, null, null, false); } @@ -587,6 +590,7 @@ public JetStreamSubscription subscribe(String subscribeSubject, String queue, Pu */ @Override public JetStreamSubscription subscribe(String subscribeSubject, Dispatcher dispatcher, MessageHandler handler, boolean autoAck) throws IOException, JetStreamApiException { + subscribeSubject = validateSubject(subscribeSubject, false); validateNotNull(dispatcher, "Dispatcher"); validateNotNull(handler, "Handler"); return createSubscription(subscribeSubject, null, null, null, (NatsDispatcher) dispatcher, handler, autoAck); @@ -597,6 +601,7 @@ public JetStreamSubscription subscribe(String subscribeSubject, Dispatcher dispa */ @Override public JetStreamSubscription subscribe(String subscribeSubject, Dispatcher dispatcher, MessageHandler handler, boolean autoAck, PushSubscribeOptions options) throws IOException, JetStreamApiException { + subscribeSubject = validateSubject(subscribeSubject, false); validateNotNull(dispatcher, "Dispatcher"); validateNotNull(handler, "Handler"); return createSubscription(subscribeSubject, options, null, null, (NatsDispatcher) dispatcher, handler, autoAck); @@ -607,7 +612,8 @@ public JetStreamSubscription subscribe(String subscribeSubject, Dispatcher dispa */ @Override public JetStreamSubscription subscribe(String subscribeSubject, String queue, Dispatcher dispatcher, MessageHandler handler, boolean autoAck, PushSubscribeOptions options) throws IOException, JetStreamApiException { - queue = validateQueueName(emptyAsNull(queue), false); + subscribeSubject = validateSubject(subscribeSubject, false); + validateQueueName(queue, false); validateNotNull(dispatcher, "Dispatcher"); validateNotNull(handler, "Handler"); return createSubscription(subscribeSubject, options, null, queue, (NatsDispatcher) dispatcher, handler, autoAck); @@ -618,6 +624,7 @@ public JetStreamSubscription subscribe(String subscribeSubject, String queue, Di */ @Override public JetStreamSubscription subscribe(String subscribeSubject, PullSubscribeOptions options) throws IOException, JetStreamApiException { + subscribeSubject = validateSubject(subscribeSubject, false); validateNotNull(options, "Pull Subscribe Options"); return createSubscription(subscribeSubject, null, options, null, null, null, false); } @@ -627,6 +634,7 @@ public JetStreamSubscription subscribe(String subscribeSubject, PullSubscribeOpt */ @Override public JetStreamSubscription subscribe(String subscribeSubject, Dispatcher dispatcher, MessageHandler handler, PullSubscribeOptions options) throws IOException, JetStreamApiException { + subscribeSubject = validateSubject(subscribeSubject, false); validateNotNull(dispatcher, "Dispatcher"); validateNotNull(handler, "Handler"); validateNotNull(options, "Pull Subscribe Options"); diff --git a/src/main/java/io/nats/client/support/Validator.java b/src/main/java/io/nats/client/support/Validator.java index 7d4d4bbbe..2fe579727 100644 --- a/src/main/java/io/nats/client/support/Validator.java +++ b/src/main/java/io/nats/client/support/Validator.java @@ -17,41 +17,30 @@ import java.time.Duration; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import java.util.regex.Pattern; import static io.nats.client.support.NatsConstants.DOT; import static io.nats.client.support.NatsJetStreamConstants.MAX_HISTORY_PER_KEY; +@SuppressWarnings("UnusedReturnValue") public abstract class Validator { - private Validator() { - } /* ensures cannot be constructed */ - - public static String validateSubject(String s, boolean required) { - return validateSubject(s, "Subject", required); - } - - public static String validateSubject(String subject, String label, boolean required, boolean cantEndWithGt) { - subject = validateSubject(subject, label, required); - if (cantEndWithGt && subject.endsWith(".>")) { - throw new IllegalArgumentException(label + " last segment cannot be '>'"); - } - return subject; - } + private Validator() {} /* ensures cannot be constructed */ /* cannot contain spaces \r \n \t cannot start or with subject token delimiter . some things don't allow it to end greater */ - public static String validateSubject(String subject, String label, boolean required) { - if (emptyAsNull(subject) == null) { + public static String validateSubjectTerm(String subject, String label, boolean required) { + subject = emptyAsNull(subject); + if (subject == null) { if (required) { throw new IllegalArgumentException(label + " cannot be null or empty."); } return null; } - subject = subject.trim(); String[] segments = subject.split("\\."); for (int seg = 0; seg < segments.length; seg++) { String segment = segments[seg]; @@ -69,7 +58,8 @@ public static String validateSubject(String subject, String label, boolean requi case 32: case '\r': case '\n': - throw new IllegalArgumentException(label + " cannot contain space, carriage return or linefeed character"); + case '\t': + throw new IllegalArgumentException(label + " cannot contain space, tab, carriage return or linefeed character"); case '*': case '>': if (sl != 1) { @@ -83,12 +73,24 @@ public static String validateSubject(String subject, String label, boolean requi return subject; } + public static String validateSubject(String s, boolean required) { + return validateSubjectTerm(s, "Subject", required); + } + + public static String validateSubject(String subject, String label, boolean required, boolean cantEndWithGt) { + subject = validateSubjectTerm(subject, label, required); + if (subject != null && cantEndWithGt && subject.endsWith(".>")) { + throw new IllegalArgumentException(label + " last segment cannot be '>'"); + } + return subject; + } + public static String validateReplyTo(String s, boolean required) { return validatePrintableExceptWildGt(s, "Reply To", required); } public static String validateQueueName(String s, boolean required) { - return validatePrintableExceptWildDotGt(s, "Queue", required); + return validateSubjectTerm(s, "QueueName", required); } public static String validateStreamName(String s, boolean required) { @@ -151,10 +153,6 @@ public static String validateMustMatchIfBothSupplied(String s1, String s2, NatsJ throw err.instance(); } - interface Check { - String check(); - } - public static String required(String s, String label) { if (emptyAsNull(s) == null) { throw new IllegalArgumentException(label + " cannot be null or empty."); @@ -188,14 +186,14 @@ public static void required(Map m, String label) { } } - public static String _validate(String s, boolean required, String label, Check check) { + public static String _validate(String s, boolean required, String label, Supplier customValidate) { if (emptyAsNull(s) == null) { if (required) { throw new IllegalArgumentException(label + " cannot be null or empty."); } return null; } - return check.check(); + return customValidate.get(); } public static String validateMaxLength(String s, int maxLength, boolean required, String label) { diff --git a/src/main/java/io/nats/service/Endpoint.java b/src/main/java/io/nats/service/Endpoint.java index cbbf3d995..1914cca38 100644 --- a/src/main/java/io/nats/service/Endpoint.java +++ b/src/main/java/io/nats/service/Endpoint.java @@ -16,6 +16,7 @@ import io.nats.client.support.JsonSerializable; import io.nats.client.support.JsonUtils; import io.nats.client.support.JsonValue; +import io.nats.client.support.Validator; import java.util.HashMap; import java.util.Map; @@ -26,7 +27,6 @@ import static io.nats.client.support.JsonValueUtils.readString; import static io.nats.client.support.JsonValueUtils.readStringStringMap; import static io.nats.client.support.Validator.validateIsRestrictedTerm; -import static io.nats.client.support.Validator.validateSubject; /** * Endpoint encapsulates the name, subject and metadata for a {@link ServiceEndpoint}. @@ -85,7 +85,7 @@ public Endpoint(String name, String subject, Map metadata) { this.subject = this.name; } else { - this.subject = validateSubject(subject, "Endpoint Subject", false); + this.subject = Validator.validateSubjectTerm(subject, "Endpoint Subject", false); } } else { diff --git a/src/main/java/io/nats/service/Group.java b/src/main/java/io/nats/service/Group.java index 0c692e067..d6da0a8f7 100644 --- a/src/main/java/io/nats/service/Group.java +++ b/src/main/java/io/nats/service/Group.java @@ -13,11 +13,12 @@ package io.nats.service; +import io.nats.client.support.Validator; + import java.util.Objects; import static io.nats.client.support.NatsConstants.DOT; import static io.nats.client.support.Validator.emptyAsNull; -import static io.nats.client.support.Validator.validateSubject; /** * Group is way to organize endpoints by serving as a common prefix to all endpoints registered in it. @@ -41,7 +42,7 @@ public Group(String name) { throw new IllegalArgumentException("Group name cannot contain '>'."); } - this.name = validateSubject(name, "Group name", false); + this.name = Validator.validateSubjectTerm(name, "Group name", false); } /** diff --git a/src/test/java/io/nats/client/SubscriberTests.java b/src/test/java/io/nats/client/SubscriberTests.java index 34aae72a9..968c8c003 100644 --- a/src/test/java/io/nats/client/SubscriberTests.java +++ b/src/test/java/io/nats/client/SubscriberTests.java @@ -20,8 +20,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeoutException; -import static io.nats.client.utils.TestBase.standardCloseConnection; -import static io.nats.client.utils.TestBase.standardConnectionWait; +import static io.nats.client.utils.TestBase.*; import static org.junit.jupiter.api.Assertions.*; public class SubscriberTests { @@ -471,27 +470,21 @@ public void testUnsubscribeWhileWaiting() { } @Test - public void testWhiteSpaceInSubject() throws Exception { + public void testInvalidSubjectsAndQueueNames() throws Exception { try (NatsTestServer ts = new NatsTestServer(false); Connection nc = Nats.connect(ts.getURI())) { standardConnectionWait(nc); - assertThrows(IllegalArgumentException.class, () -> nc.subscribe("foo bar")); - assertThrows(IllegalArgumentException.class, () -> nc.subscribe("foo\tbar")); - assertThrows(IllegalArgumentException.class, () -> nc.subscribe("foo ")); - assertThrows(IllegalArgumentException.class, () -> nc.subscribe(" foo")); - assertThrows(IllegalArgumentException.class, () -> nc.subscribe(" foo")); - } - } - - @Test - public void testWhiteSpaceInQueue() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - assertThrows(IllegalArgumentException.class, () -> nc.subscribe("test", "foo bar")); - assertThrows(IllegalArgumentException.class, () -> nc.subscribe("test", "foo\tbar")); - assertThrows(IllegalArgumentException.class, () -> nc.subscribe("test", "foo ")); - assertThrows(IllegalArgumentException.class, () -> nc.subscribe("test", " foo")); + Dispatcher d = nc.createDispatcher(); + for (String bad : BAD_SUBJECTS_OR_QUEUES) { + assertThrows(IllegalArgumentException.class, () -> nc.subscribe(bad)); + assertThrows(IllegalArgumentException.class, () -> d.subscribe(bad)); + assertThrows(IllegalArgumentException.class, () -> d.subscribe(bad, m -> {})); + assertThrows(IllegalArgumentException.class, () -> d.subscribe(bad, "q")); + assertThrows(IllegalArgumentException.class, () -> d.subscribe(bad, "q", m -> {})); + assertThrows(IllegalArgumentException.class, () -> nc.subscribe("s", bad)); + assertThrows(IllegalArgumentException.class, () -> d.subscribe("s", bad)); + assertThrows(IllegalArgumentException.class, () -> d.subscribe("s", bad, m -> {})); + } } } } \ No newline at end of file diff --git a/src/test/java/io/nats/client/impl/JetStreamGeneralTests.java b/src/test/java/io/nats/client/impl/JetStreamGeneralTests.java index cdc21c050..c373a6d3c 100644 --- a/src/test/java/io/nats/client/impl/JetStreamGeneralTests.java +++ b/src/test/java/io/nats/client/impl/JetStreamGeneralTests.java @@ -228,9 +228,9 @@ public void testJetStreamSubscribeLenientSubject() throws Exception { Dispatcher d = nc.createDispatcher(); js.subscribe(SUBJECT, (PushSubscribeOptions)null); - js.subscribe(SUBJECT, null, (PushSubscribeOptions)null); + js.subscribe(SUBJECT, null, (PushSubscribeOptions)null); // queue name is not required, just a weird way to call this api js.subscribe(SUBJECT, d, m -> {}, false, (PushSubscribeOptions)null); - js.subscribe(SUBJECT, null, d, m -> {}, false, (PushSubscribeOptions)null); + js.subscribe(SUBJECT, null, d, m -> {}, false, (PushSubscribeOptions)null); // queue name is not required, just a weird way to call this api PushSubscribeOptions pso = ConsumerConfiguration.builder().filterSubject(SUBJECT).buildPushSubscribeOptions(); js.subscribe(null, pso); @@ -274,20 +274,24 @@ public void testJetStreamSubscribeErrors() throws Exception { PushSubscribeOptions psoInvalidStream = PushSubscribeOptions.builder().stream(STREAM).build(); assertThrows(JetStreamApiException.class, () -> js.subscribe(SUBJECT, psoInvalidStream)); - // subject - IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(null)); - assertTrue(iae.getMessage().startsWith("Subject")); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, (PushSubscribeOptions)null)); - assertTrue(iae.getMessage().startsWith("Subject")); + Dispatcher d = nc.createDispatcher(); - // queue - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, HAS_SPACE, null)); - assertTrue(iae.getMessage().startsWith("Queue")); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, HAS_SPACE, null, null, false, null)); - assertTrue(iae.getMessage().startsWith("Queue")); + for (String bad : BAD_SUBJECTS_OR_QUEUES) { + // subject + IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(bad)); + assertTrue(iae.getMessage().startsWith("Subject")); + iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(bad, (PushSubscribeOptions)null)); + assertTrue(iae.getMessage().startsWith("Subject")); + + // queue + iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, bad, null)); + assertTrue(iae.getMessage().startsWith("Queue")); + iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, bad, d, m -> {}, false, null)); + assertTrue(iae.getMessage().startsWith("Queue")); + } // dispatcher - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, null, null, false)); + IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, null, null, false)); assertTrue(iae.getMessage().startsWith("Dispatcher")); iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, null, null, false, null)); assertTrue(iae.getMessage().startsWith("Dispatcher")); diff --git a/src/test/java/io/nats/client/support/ValidatorTests.java b/src/test/java/io/nats/client/support/ValidatorTests.java index eddfe602e..e83ee953c 100644 --- a/src/test/java/io/nats/client/support/ValidatorTests.java +++ b/src/test/java/io/nats/client/support/ValidatorTests.java @@ -62,9 +62,9 @@ public void testValidateReplyTo() { @Test public void testValidateQueueName() { // validateQueueName(String s, boolean required) - allowedRequired(Validator::validateQueueName, Arrays.asList(PLAIN, HAS_PRINTABLE, HAS_DOLLAR)); - notAllowedRequired(Validator::validateQueueName, Arrays.asList(null, EMPTY, HAS_SPACE, HAS_DOT, STAR_NOT_SEGMENT, GT_NOT_SEGMENT, HAS_LOW, HAS_127)); - notAllowedRequired(Validator::validateQueueName, UTF_ONLY_STRINGS); + allowedRequired(Validator::validateQueueName, Arrays.asList(PLAIN, HAS_PRINTABLE, HAS_DOLLAR, HAS_DOT, HAS_LOW, HAS_127)); + notAllowedRequired(Validator::validateQueueName, Arrays.asList(null, EMPTY, HAS_SPACE, STAR_NOT_SEGMENT, GT_NOT_SEGMENT)); + allowedRequired(Validator::validateQueueName, UTF_ONLY_STRINGS); allowedNotRequiredEmptyAsNull(Validator::validateQueueName, Arrays.asList(null, EMPTY)); } diff --git a/src/test/java/io/nats/client/utils/TestBase.java b/src/test/java/io/nats/client/utils/TestBase.java index 2143978b5..b5c7ec1a2 100644 --- a/src/test/java/io/nats/client/utils/TestBase.java +++ b/src/test/java/io/nats/client/utils/TestBase.java @@ -31,6 +31,7 @@ import java.util.function.Supplier; import static io.nats.client.support.NatsConstants.DOT; +import static io.nats.client.support.NatsConstants.EMPTY; import static io.nats.client.support.NatsJetStreamClientError.KIND_ILLEGAL_ARGUMENT; import static io.nats.client.support.NatsJetStreamClientError.KIND_ILLEGAL_STATE; import static io.nats.examples.ExampleUtils.uniqueEnough; @@ -46,6 +47,8 @@ public class TestBase { public static final String PLAIN = "plain"; public static final String HAS_SPACE = "has space"; + public static final String STARTS_SPACE = " startsspace"; + public static final String ENDS_SPACE = " endssspace "; public static final String HAS_PRINTABLE = "has-print!able"; public static final String HAS_DOT = "has.dot"; public static final String HAS_DASH = "has-dash"; @@ -53,6 +56,7 @@ public class TestBase { public static final String HAS_DOLLAR = "has$dollar"; public static final String HAS_CR = "has\rcr"; public static final String HAS_LF = "has\nlf"; + public static final String HAS_TAB = "has\ttab"; public static final String HAS_LOW = "has" + (char)0 + "low"; public static final String HAS_127 = "has" + (char)127 + "127"; public static final String HAS_FWD_SLASH = "has/fwd/slash"; @@ -67,6 +71,10 @@ public class TestBase { public static final long LONG_TIMEOUT_MS = 15000; public static final long VERY_LONG_TIMEOUT_MS = 20000; + public static String[] BAD_SUBJECTS_OR_QUEUES = new String[] { + HAS_SPACE, HAS_CR, HAS_LF, HAS_TAB, STARTS_SPACE, ENDS_SPACE, null, EMPTY + }; + static { NatsTestServer.quiet(); }