diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java index 220b81b5daa..741b5582ff2 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java @@ -23,6 +23,7 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketTimeoutException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.concurrent.CountDownLatch; @@ -43,12 +44,14 @@ import org.eclipse.jetty.io.ssl.SslHandshakeListener; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.JavaVersion; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.ExecutorThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Assert; +import org.junit.Assume; import org.junit.Test; public class HttpClientTLSTest @@ -245,6 +248,10 @@ public class HttpClientTLSTest @Test public void testMismatchBetweenTLSProtocolAndTLSCiphersOnClient() throws Exception { + // In JDK 11, a mismatch on the client does not generate any bytes towards + // the server, while in TLS 1.2 the client sends to the server the close_notify. + Assume.assumeThat(JavaVersion.VERSION.getPlatform(), Matchers.lessThan(11)); + SslContextFactory serverTLSFactory = createSslContextFactory(); startServer(serverTLSFactory, new EmptyServerHandler()); @@ -330,6 +337,9 @@ public class HttpClientTLSTest @Test public void testHandshakeSucceededWithSessionResumption() throws Exception { + // Excluded because of a bug in JDK 11+27 where session resumption does not work. + Assume.assumeThat(JavaVersion.VERSION.getPlatform(), Matchers.lessThan(11)); + SslContextFactory serverTLSFactory = createSslContextFactory(); startServer(serverTLSFactory, new EmptyServerHandler()); @@ -407,6 +417,9 @@ public class HttpClientTLSTest @Test public void testClientRawCloseDoesNotInvalidateSession() throws Exception { + // Excluded because of a bug in JDK 11+27 where session resumption does not work. + Assume.assumeThat(JavaVersion.VERSION.getPlatform(), Matchers.lessThan(11)); + SslContextFactory serverTLSFactory = createSslContextFactory(); startServer(serverTLSFactory, new EmptyServerHandler()); @@ -427,6 +440,17 @@ public class HttpClientTLSTest sslSocket.startHandshake(); Assert.assertTrue(handshakeLatch1.await(5, TimeUnit.SECONDS)); + // In TLS 1.3 the server sends a NewSessionTicket post-handshake message + // to enable session resumption and without a read, the message is not processed. + try + { + sslSocket.setSoTimeout(1000); + sslSocket.getInputStream().read(); + } + catch (SocketTimeoutException expected) + { + } + // The client closes abruptly. socket.close(); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/NeedWantClientAuthTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/NeedWantClientAuthTest.java index 1f9333d7aef..9ecb42b04da 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/NeedWantClientAuthTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/NeedWantClientAuthTest.java @@ -22,6 +22,7 @@ import java.security.cert.Certificate; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSession; @@ -96,9 +97,7 @@ public class NeedWantClientAuthTest @Test public void testWantClientAuthWithoutAuth() throws Exception { - SslContextFactory serverSSL = new SslContextFactory(); - serverSSL.setKeyStorePath("src/test/resources/keystore.jks"); - serverSSL.setKeyStorePassword("storepwd"); + SslContextFactory serverSSL = createSslContextFactory(); serverSSL.setWantClientAuth(true); startServer(serverSSL, new EmptyServerHandler()); @@ -115,9 +114,7 @@ public class NeedWantClientAuthTest @Test public void testWantClientAuthWithAuth() throws Exception { - SslContextFactory serverSSL = new SslContextFactory(); - serverSSL.setKeyStorePath("src/test/resources/keystore.jks"); - serverSSL.setKeyStorePassword("storepwd"); + SslContextFactory serverSSL = createSslContextFactory(); serverSSL.setWantClientAuth(true); startServer(serverSSL, new EmptyServerHandler()); CountDownLatch handshakeLatch = new CountDownLatch(1); @@ -157,9 +154,14 @@ public class NeedWantClientAuthTest @Test public void testNeedClientAuthWithoutAuth() throws Exception { - SslContextFactory serverSSL = new SslContextFactory(); - serverSSL.setKeyStorePath("src/test/resources/keystore.jks"); - serverSSL.setKeyStorePassword("storepwd"); + // In TLS 1.2, the TLS handshake on the client finishes after the TLS handshake on the server. + // The server detects the lack of the client certificate, fails its TLS handshake and sends + // bad_certificate to the client, which then fails its own TLS handshake. + // In TLS 1.3, the TLS handshake on the client finishes before the TLS handshake on the server. + // The server still sends bad_certificate to the client, but the client handshake has already + // completed successfully its TLS handshake. + + SslContextFactory serverSSL = createSslContextFactory(); serverSSL.setNeedClientAuth(true); startServer(serverSSL, new EmptyServerHandler()); @@ -168,6 +170,13 @@ public class NeedWantClientAuthTest CountDownLatch handshakeLatch = new CountDownLatch(1); client.addBean(new SslHandshakeListener() { + @Override + public void handshakeSucceeded(Event event) + { + if ("TLSv1.3".equals(event.getSSLEngine().getSession().getProtocol())) + handshakeLatch.countDown(); + } + @Override public void handshakeFailed(Event event, Throwable failure) { @@ -182,7 +191,11 @@ public class NeedWantClientAuthTest .send(result -> { if (result.isFailed()) - latch.countDown(); + { + Throwable failure = result.getFailure(); + if (failure instanceof SSLException) + latch.countDown(); + } }); Assert.assertTrue(handshakeLatch.await(5, TimeUnit.SECONDS)); @@ -192,9 +205,7 @@ public class NeedWantClientAuthTest @Test public void testNeedClientAuthWithAuth() throws Exception { - SslContextFactory serverSSL = new SslContextFactory(); - serverSSL.setKeyStorePath("src/test/resources/keystore.jks"); - serverSSL.setKeyStorePassword("storepwd"); + SslContextFactory serverSSL = createSslContextFactory(); serverSSL.setNeedClientAuth(true); startServer(serverSSL, new EmptyServerHandler()); CountDownLatch handshakeLatch = new CountDownLatch(1); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesClientTest.java index 64610fbfb7a..d9a08b5d5dc 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesClientTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesClientTest.java @@ -42,9 +42,12 @@ import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.JavaVersion; import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.hamcrest.Matchers; import org.junit.After; import org.junit.Assert; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; @@ -59,17 +62,20 @@ public class SslBytesClientTest extends SslBytesTest @Before public void init() throws Exception { + // This whole test is very specific to how TLS < 1.3 works. + Assume.assumeThat(JavaVersion.VERSION.getPlatform(), Matchers.lessThan(11)); + threadPool = Executors.newCachedThreadPool(); - client = new HttpClient(new SslContextFactory(true)); + sslContextFactory = new SslContextFactory(true); + client = new HttpClient(sslContextFactory); client.setMaxConnectionsPerDestination(1); File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks"); - sslContextFactory = client.getSslContextFactory(); sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath()); sslContextFactory.setKeyStorePassword("storepwd"); client.start(); - SSLContext sslContext = sslContextFactory.getSslContext(); + SSLContext sslContext = this.sslContextFactory.getSslContext(); acceptor = (SSLServerSocket)sslContext.getServerSocketFactory().createServerSocket(0); int serverPort = acceptor.getLocalPort(); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java index 690c4d826e0..f891e712896 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java @@ -67,6 +67,7 @@ import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.OS; +import org.eclipse.jetty.util.JavaVersion; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.hamcrest.Matchers; @@ -94,6 +95,9 @@ public class SslBytesServerTest extends SslBytesTest @Before public void init() throws Exception { + // This whole test is very specific to how TLS < 1.3 works. + Assume.assumeThat(JavaVersion.VERSION.getPlatform(), Matchers.lessThan(11)); + threadPool = Executors.newCachedThreadPool(); server = new Server(); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 4866bcd7764..3b1d00c573b 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -79,8 +79,7 @@ import org.eclipse.jetty.util.thread.Invocable; public class SslConnection extends AbstractConnection { private static final Logger LOG = Log.getLogger(SslConnection.class); - - // TODO reduce the about of debug + private static final String TLS_1_3 = "TLSv1.3"; private enum Handshake { @@ -597,9 +596,8 @@ public class SslConnection extends AbstractConnection { if (unwrapResult.getHandshakeStatus() == HandshakeStatus.FINISHED) handshakeSucceeded(); - - // Check whether re-negotiation is allowed - if (!allowRenegotiate(_sslEngine.getHandshakeStatus())) + + if (isRenegotiating() && !allowRenegotiate()) return filled = -1; // If bytes were produced, don't bother with the handshake status; @@ -911,7 +909,7 @@ public class SslConnection extends AbstractConnection if (wrapResult.getHandshakeStatus() == HandshakeStatus.FINISHED) handshakeSucceeded(); - if (!allowRenegotiate(_sslEngine.getHandshakeStatus())) + if (isRenegotiating() && !allowRenegotiate()) { getEndPoint().shutdownOutput(); if (allConsumed && BufferUtil.isEmpty(_encryptedOutput)) @@ -1053,28 +1051,25 @@ public class SslConnection extends AbstractConnection { try { + boolean close; boolean flush = false; - boolean close = false; synchronized(_decryptedEndPoint) { - boolean ishut = isInputShutdown(); - boolean oshut = isOutputShutdown(); + boolean ishut = getEndPoint().isInputShutdown(); + boolean oshut = getEndPoint().isOutputShutdown(); if (LOG.isDebugEnabled()) LOG.debug("shutdownOutput: {} oshut={}, ishut={} {}", SslConnection.this, oshut, ishut); - if (oshut) - return; + closeOutbound(); if (!_closedOutbound) { - _closedOutbound=true; // Only attempt this once - closeOutbound(); - flush = true; + _closedOutbound = true; + // Flush only once. + flush = !oshut; } - // TODO review close logic here - if (ishut) - close = true; + close = ishut; } if (flush) @@ -1199,17 +1194,19 @@ public class SslConnection extends AbstractConnection } } - @Override - public String toString() + private boolean isRenegotiating() { - return super.toEndPointString(); + if (_handshake.get() == Handshake.INITIAL) + return false; + if (isTLS13()) + return false; + if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) + return false; + return true; } - private boolean allowRenegotiate(HandshakeStatus handshakeStatus) - { - if (_handshake.get() == Handshake.INITIAL || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING) - return true; - + private boolean allowRenegotiate() + { if (!isRenegotiationAllowed()) { if (LOG.isDebugEnabled()) @@ -1217,7 +1214,7 @@ public class SslConnection extends AbstractConnection terminateInput(); return false; } - + if (getRenegotiationLimit()==0) { if (LOG.isDebugEnabled()) @@ -1225,10 +1222,22 @@ public class SslConnection extends AbstractConnection terminateInput(); return false; } - + return true; } + private boolean isTLS13() + { + String protocol = _sslEngine.getSession().getProtocol(); + return TLS_1_3.equals(protocol); + } + + @Override + public String toString() + { + return super.toEndPointString(); + } + private final class IncompleteWriteCallback implements Callback, Invocable { @Override diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/SslConnectionTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/SslConnectionTest.java index fcd320e2448..c8176678282 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/SslConnectionTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/SslConnectionTest.java @@ -47,19 +47,17 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.util.thread.TimerScheduler; import org.junit.After; -import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; public class SslConnectionTest { - private final static int TIMEOUT = 1000000; - private static SslContextFactory __sslCtxFactory=new SslContextFactory(); + private static final int TIMEOUT = 1000000; private static ByteBufferPool __byteBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()); + private final SslContextFactory _sslCtxFactory =new SslContextFactory(); protected volatile EndPoint _lastEndp; private volatile boolean _testFill=true; private volatile FutureCallback _writeCallback; @@ -81,11 +79,11 @@ public class SslConnectionTest @Override public Connection newConnection(SelectableChannel channel, EndPoint endpoint, Object attachment) { - SSLEngine engine = __sslCtxFactory.newSSLEngine(); + SSLEngine engine = _sslCtxFactory.newSSLEngine(); engine.setUseClientMode(false); SslConnection sslConnection = new SslConnection(__byteBufferPool, getExecutor(), endpoint, engine); - sslConnection.setRenegotiationAllowed(__sslCtxFactory.isRenegotiationAllowed()); - sslConnection.setRenegotiationLimit(__sslCtxFactory.getRenegotiationLimit()); + sslConnection.setRenegotiationAllowed(_sslCtxFactory.isRenegotiationAllowed()); + sslConnection.setRenegotiationLimit(_sslCtxFactory.getRenegotiationLimit()); Connection appConnection = new TestConnection(sslConnection.getDecryptedEndPoint()); sslConnection.getDecryptedEndPoint().setConnection(appConnection); return sslConnection; @@ -131,26 +129,20 @@ public class SslConnectionTest return super.flush(buffers); } } - - - @BeforeClass - public static void initSslEngine() throws Exception - { - File keystore = MavenTestingUtils.getTestResourceFile("keystore"); - __sslCtxFactory.setKeyStorePath(keystore.getAbsolutePath()); - __sslCtxFactory.setKeyStorePassword("storepwd"); - __sslCtxFactory.setKeyManagerPassword("keypwd"); - __sslCtxFactory.start(); - } - - @AfterClass - public static void stopSsl() throws Exception - { - __sslCtxFactory.stop(); - } @Before - public void startManager() throws Exception + public void initSSL() throws Exception + { + File keystore = MavenTestingUtils.getTestResourceFile("keystore"); + _sslCtxFactory.setKeyStorePath(keystore.getAbsolutePath()); + _sslCtxFactory.setKeyStorePassword("storepwd"); + _sslCtxFactory.setKeyManagerPassword("keypwd"); + _sslCtxFactory.setRenegotiationAllowed(true); + _sslCtxFactory.setRenegotiationLimit(-1); + startManager(); + } + + private void startManager() throws Exception { _testFill=true; _writeCallback=null; @@ -160,15 +152,23 @@ public class SslConnectionTest _threadPool.start(); _scheduler.start(); _manager.start(); - __sslCtxFactory.setRenegotiationAllowed(true); - __sslCtxFactory.setRenegotiationLimit(-1); + } + private void startSSL() throws Exception + { + _sslCtxFactory.start(); } @After - public void stopManager() throws Exception + public void stopSSL() throws Exception { - if (_lastEndp.isOpen()) + stopManager(); + _sslCtxFactory.stop(); + } + + private void stopManager() throws Exception + { + if (_lastEndp != null && _lastEndp.isOpen()) _lastEndp.close(); _manager.stop(); _scheduler.stop(); @@ -253,9 +253,10 @@ public class SslConnectionTest } } } + protected SSLSocket newClient() throws IOException { - SSLSocket socket = __sslCtxFactory.newSslSocket(); + SSLSocket socket = _sslCtxFactory.newSslSocket(); socket.connect(_connector.socket().getLocalSocketAddress()); return socket; } @@ -263,6 +264,7 @@ public class SslConnectionTest @Test public void testHelloWorld() throws Exception { + startSSL(); try (Socket client = newClient()) { client.setSoTimeout(TIMEOUT); @@ -289,6 +291,7 @@ public class SslConnectionTest @Test public void testRenegotiate() throws Exception { + startSSL(); try (SSLSocket client = newClient()) { client.setSoTimeout(TIMEOUT); @@ -316,8 +319,10 @@ public class SslConnectionTest @Test public void testRenegotiateNotAllowed() throws Exception { - __sslCtxFactory.setRenegotiationAllowed(false); - + // TLS 1.3 and beyond do not support renegotiation. + _sslCtxFactory.setIncludeProtocols("TLSv1.2"); + _sslCtxFactory.setRenegotiationAllowed(false); + startSSL(); try (SSLSocket client = newClient()) { client.setSoTimeout(TIMEOUT); @@ -332,6 +337,7 @@ public class SslConnectionTest Assert.assertEquals(5, len); Assert.assertEquals("Hello", new String(buffer, 0, len, StandardCharsets.UTF_8)); + // Try to renegotiate, must fail. client.startHandshake(); client.getOutputStream().write("World".getBytes(StandardCharsets.UTF_8)); @@ -351,9 +357,11 @@ public class SslConnectionTest @Test public void testRenegotiateLimit() throws Exception { - __sslCtxFactory.setRenegotiationAllowed(true); - __sslCtxFactory.setRenegotiationLimit(2); - + // TLS 1.3 and beyond do not support renegotiation. + _sslCtxFactory.setIncludeProtocols("TLSv1.2"); + _sslCtxFactory.setRenegotiationAllowed(true); + _sslCtxFactory.setRenegotiationLimit(2); + startSSL(); try (SSLSocket client = newClient()) { client.setSoTimeout(TIMEOUT); @@ -403,7 +411,7 @@ public class SslConnectionTest { _testFill=false; _writeCallback = new FutureCallback(); - + startSSL(); try (SSLSocket client = newClient()) { client.setSoTimeout(TIMEOUT); @@ -428,6 +436,7 @@ public class SslConnectionTest @Test public void testBlockedWrite() throws Exception { + startSSL(); try (Socket client = newClient()) { client.setSoTimeout(5000); @@ -458,6 +467,7 @@ public class SslConnectionTest @Test public void testManyLines() throws Exception { + startSSL(); try (Socket client = newClient()) { client.setSoTimeout(10000); diff --git a/jetty-jaspi/pom.xml b/jetty-jaspi/pom.xml index 8e0376714b3..d94ebe39dfd 100644 --- a/jetty-jaspi/pom.xml +++ b/jetty-jaspi/pom.xml @@ -4,14 +4,16 @@ jetty-project 9.4.12-SNAPSHOT + 4.0.0 jetty-jaspi Jetty :: JASPI Security Jetty security infrastructure - http://www.eclipse.org/jetty + ${project.groupId}.security.jaspi + @@ -23,24 +25,7 @@ - - - jdk9 - - [1.9,) - - - - - maven-surefire-plugin - - @{argLine} --add-modules java.se.ee - - - - - - + org.eclipse.jetty @@ -56,6 +41,24 @@ org.eclipse.jetty.orbit javax.security.auth.message + + javax.xml.bind + jaxb-api + 2.3.0 + test + + + org.glassfish.jaxb + jaxb-runtime + 2.3.0.1 + test + + + javax.activation + javax.activation-api + 1.2.0 + test + org.apache.geronimo.components geronimo-jaspi diff --git a/jetty-server/src/test/resources/snikeystore b/jetty-server/src/test/resources/snikeystore index 3c69266e062..fab22bd8eb8 100644 Binary files a/jetty-server/src/test/resources/snikeystore and b/jetty-server/src/test/resources/snikeystore differ diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/HttpInputIntegrationTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/HttpInputIntegrationTest.java index cde27abaad0..1fe4828923c 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/HttpInputIntegrationTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/HttpInputIntegrationTest.java @@ -59,10 +59,12 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.JavaVersion; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.hamcrest.Matchers; import org.junit.AfterClass; import org.junit.Assert; +import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -74,12 +76,12 @@ import static org.junit.Assert.assertThat; @RunWith(Parameterized.class) public class HttpInputIntegrationTest { - + enum Mode { BLOCKING, ASYNC_DISPATCHED, ASYNC_OTHER_DISPATCHED, ASYNC_OTHER_WAIT } public final static String EOF = "__EOF__"; public final static String DELAY = "__DELAY__"; public final static String ABORT = "__ABORT__"; - + private static Server __server; private static HttpConfiguration __config; private static HttpConfiguration __sslConfig; @@ -342,10 +344,13 @@ public class HttpInputIntegrationTest assertThat(response,Matchers.containsString("read="+_read)); assertThat(response,Matchers.containsString("sum="+sum)); } - + @Test public void testStress() throws Exception { + // JDK 11's SSLSocket is not reliable enough to run this test. + Assume.assumeThat(JavaVersion.VERSION.getPlatform(), Matchers.lessThan(11)); + System.err.printf("[%d] STRESS c=%s, m=%s, delayDispatch=%b delayInFrame=%s content-length:%d expect=%d read=%d content:%s%n",_id,_client.getSimpleName(),_mode,__config.isDelayDispatchUntilContent(),_delay,_length,_status,_read,_send); int sum=0;