8231449: HttpClient’s client ssl certificate authentication seems to be broken.
authordfuchs
Wed, 16 Oct 2019 14:50:53 +0100
changeset 58649 6b6bf0de534b
parent 58644 64597a6fd186
child 58650 d068b1e534de
8231449: HttpClient’s client ssl certificate authentication seems to be broken. Summary: SSLFlowDelegate.Reader and SubscriberWrapper are changed to better cooperate on when more demand should be requested from upstream. The issue encountered in this scenario was triggered by a large certificate which caused the SSLFlowDelegate to stop requesting data from upstream during the handshake although the engine handshake status was NEED_UNWRAP. Reviewed-by: chegar
src/java.net.http/share/classes/jdk/internal/net/http/common/SSLFlowDelegate.java
src/java.net.http/share/classes/jdk/internal/net/http/common/SubscriberWrapper.java
test/jdk/java/net/httpclient/HttpSlowServerTest.java
test/jdk/java/net/httpclient/LargeHandshakeTest.java
test/jdk/java/net/httpclient/LargeResponseTest.java
--- a/src/java.net.http/share/classes/jdk/internal/net/http/common/SSLFlowDelegate.java	Wed Oct 16 12:36:44 2019 +0200
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/SSLFlowDelegate.java	Wed Oct 16 14:50:53 2019 +0100
@@ -318,14 +318,19 @@
 
         @Override
         protected long upstreamWindowUpdate(long currentWindow, long downstreamQsize) {
-            if (readBuf.remaining() > TARGET_BUFSIZE) {
-                if (debugr.on())
-                    debugr.log("readBuf has more than TARGET_BUFSIZE: %d",
-                            readBuf.remaining());
-                return 0;
-            } else {
-                return super.upstreamWindowUpdate(currentWindow, downstreamQsize);
+            if (needsMoreData()) {
+                // run the scheduler to see if more data should be requested
+                if (debugr.on()) {
+                    int remaining = readBuf.remaining();
+                    if (remaining > TARGET_BUFSIZE) {
+                        // just some logging to check how much we have in the read buffer
+                        debugr.log("readBuf has more than TARGET_BUFSIZE: %d",
+                                remaining);
+                    }
+                }
+                scheduler.runOrSchedule();
             }
+            return 0; // we will request more from the scheduler loop (processData).
         }
 
         // readBuf is kept ready for reading outside of this method
@@ -368,6 +373,32 @@
         // we had before calling unwrap() again.
         volatile int minBytesRequired;
 
+        // We might need to request more data if:
+        //  - we have a subscription from upstream
+        //  - and we don't have enough data to decrypt in the read buffer
+        //  - *and* - either we're handshaking, and more data is required (NEED_UNWRAP),
+        //          - or we have demand from downstream, but we have nothing decrypted
+        //            to forward downstream.
+        boolean needsMoreData() {
+            if (upstreamSubscription != null && readBuf.remaining() <= minBytesRequired &&
+                    (engine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP
+                            || !downstreamSubscription.demand.isFulfilled() && hasNoOutputData())) {
+                return true;
+            }
+            return false;
+        }
+
+        // If the readBuf has not enough data, and we either need to
+        // unwrap (handshaking) or we have demand from downstream,
+        // then request more data
+        void requestMoreDataIfNeeded() {
+            if (needsMoreData()) {
+                // request more will only request more if our
+                // demand from upstream is fulfilled
+                requestMore();
+            }
+        }
+
         // work function where it all happens
         final void processData() {
             try {
@@ -434,6 +465,7 @@
                             outgoing(Utils.EMPTY_BB_LIST, true);
                             // complete ALPN if not yet completed
                             setALPN();
+                            requestMoreDataIfNeeded();
                             return;
                         }
                         if (result.handshaking()) {
@@ -451,8 +483,10 @@
                         handleError(ex);
                         return;
                     }
-                    if (handshaking && !complete)
+                    if (handshaking && !complete) {
+                        requestMoreDataIfNeeded();
                         return;
+                    }
                 }
                 if (!complete) {
                     synchronized (readBufferLock) {
@@ -466,6 +500,8 @@
                     // activity.
                     setALPN();
                     outgoing(Utils.EMPTY_BB_LIST, true);
+                } else {
+                    requestMoreDataIfNeeded();
                 }
             } catch (Throwable ex) {
                 errorCommon(ex);
--- a/src/java.net.http/share/classes/jdk/internal/net/http/common/SubscriberWrapper.java	Wed Oct 16 12:36:44 2019 +0200
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/SubscriberWrapper.java	Wed Oct 16 14:50:53 2019 +0100
@@ -26,9 +26,7 @@
 package jdk.internal.net.http.common;
 
 import java.io.Closeable;
-import java.lang.System.Logger.Level;
 import java.nio.ByteBuffer;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
@@ -318,11 +316,33 @@
                 downstreamSubscriber.onNext(b);
                 datasent = true;
             }
-            if (datasent) upstreamWindowUpdate();
+
+            // If we have sent some decrypted data downstream,
+            // or if:
+            //    - there's nothing more available to send downstream
+            //    - and we still have some demand from downstream
+            //    - and upstream is not completed yet
+            //    - and our demand from upstream has reached 0,
+            // then check whether we should request more data from
+            // upstream
+            if (datasent || outputQ.isEmpty()
+                    && !downstreamSubscription.demand.isFulfilled()
+                    && !upstreamCompleted
+                    && upstreamWindow.get() == 0) {
+                upstreamWindowUpdate();
+            }
             checkCompletion();
         }
     }
 
+    final int outputQueueSize() {
+        return outputQ.size();
+    }
+
+    final boolean hasNoOutputData() {
+        return outputQ.isEmpty();
+    }
+
     void upstreamWindowUpdate() {
         long downstreamQueueSize = outputQ.size();
         long upstreamWindowSize = upstreamWindow.get();
@@ -341,7 +361,7 @@
             throw new IllegalStateException("Single shot publisher");
         }
         this.upstreamSubscription = subscription;
-        upstreamRequest(upstreamWindowUpdate(0, 0));
+        upstreamRequest(initialUpstreamDemand());
         if (debug.on())
             debug.log("calling downstreamSubscriber::onSubscribe on %s",
                       downstreamSubscriber);
@@ -356,7 +376,6 @@
         if (prev <= 0)
             throw new IllegalStateException("invalid onNext call");
         incomingCaller(item, false);
-        upstreamWindowUpdate();
     }
 
     private void upstreamRequest(long n) {
@@ -365,6 +384,16 @@
         upstreamSubscription.request(n);
     }
 
+    /**
+     * Initial demand that should be requested
+     * from upstream when we get the upstream subscription
+     * from {@link #onSubscribe(Flow.Subscription)}.
+     * @return The initial demand to request from upstream.
+     */
+    protected long initialUpstreamDemand() {
+        return 1;
+    }
+
     protected void requestMore() {
         if (upstreamWindow.get() == 0) {
             upstreamRequest(1);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/HttpSlowServerTest.java	Wed Oct 16 14:50:53 2019 +0100
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsServer;
+import jdk.test.lib.net.SimpleSSLContext;
+
+import javax.net.ssl.SSLContext;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @test
+ * @summary This test verifies that the HttpClient works correctly when connected to a
+ *          slow server.
+ * @library /test/lib http2/server
+ * @build jdk.test.lib.net.SimpleSSLContext HttpServerAdapters DigestEchoServer HttpSlowServerTest
+ * @modules java.net.http/jdk.internal.net.http.common
+ *          java.net.http/jdk.internal.net.http.frame
+ *          java.net.http/jdk.internal.net.http.hpack
+ *          java.logging
+ *          java.base/sun.net.www.http
+ *          java.base/sun.net.www
+ *          java.base/sun.net
+ * @run main/othervm -Dtest.requiresHost=true
+ *                   -Djdk.httpclient.HttpClient.log=headers
+ *                   -Djdk.internal.httpclient.debug=false
+ *                   HttpSlowServerTest
+ *
+ */
+public class HttpSlowServerTest implements HttpServerAdapters {
+    static final List<String> data = List.of(
+            "Lorem ipsum",
+            "dolor sit amet",
+            "consectetur adipiscing elit, sed do eiusmod tempor",
+            "quis nostrud exercitation ullamco",
+            "laboris nisi",
+            "ut",
+            "aliquip ex ea commodo consequat.",
+            "Duis aute irure dolor in reprehenderit in voluptate velit esse",
+            "cillum dolore eu fugiat nulla pariatur.",
+            "Excepteur sint occaecat cupidatat non proident."
+    );
+
+    static final SSLContext context;
+    static {
+        try {
+            context = new SimpleSSLContext().get();
+            SSLContext.setDefault(context);
+        } catch (Exception x) {
+            throw new ExceptionInInitializerError(x);
+        }
+    }
+
+    final AtomicLong requestCounter = new AtomicLong();
+    final AtomicLong responseCounter = new AtomicLong();
+    HttpTestServer http1Server;
+    HttpTestServer http2Server;
+    HttpTestServer https1Server;
+    HttpTestServer https2Server;
+    DigestEchoServer.TunnelingProxy proxy;
+
+    URI http1URI;
+    URI https1URI;
+    URI http2URI;
+    URI https2URI;
+    InetSocketAddress proxyAddress;
+    ProxySelector proxySelector;
+    HttpClient client;
+    List<CompletableFuture<?>>  futures = new CopyOnWriteArrayList<>();
+    Set<URI> pending = new CopyOnWriteArraySet<>();
+
+    final ExecutorService executor = new ThreadPoolExecutor(12, 60, 10,
+            TimeUnit.SECONDS, new LinkedBlockingQueue<>()); // Shared by HTTP/1.1 servers
+    final ExecutorService clientexec = new ThreadPoolExecutor(6, 12, 1,
+            TimeUnit.SECONDS, new LinkedBlockingQueue<>()); // Used by the client
+
+    public HttpClient newHttpClient(ProxySelector ps) {
+        HttpClient.Builder builder = HttpClient
+                .newBuilder()
+                .sslContext(context)
+                .executor(clientexec)
+                .proxy(ps);
+        return builder.build();
+    }
+
+    public void setUp() throws Exception {
+        try {
+            InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
+
+            // HTTP/1.1
+            HttpServer server1 = HttpServer.create(sa, 0);
+            server1.setExecutor(executor);
+            http1Server = HttpTestServer.of(server1);
+            http1Server.addHandler(new HttpTestSlowHandler(), "/HttpSlowServerTest/http1/");
+            http1Server.start();
+            http1URI = new URI("http://" + http1Server.serverAuthority() + "/HttpSlowServerTest/http1/");
+
+
+            // HTTPS/1.1
+            HttpsServer sserver1 = HttpsServer.create(sa, 100);
+            sserver1.setExecutor(executor);
+            sserver1.setHttpsConfigurator(new HttpsConfigurator(context));
+            https1Server = HttpTestServer.of(sserver1);
+            https1Server.addHandler(new HttpTestSlowHandler(), "/HttpSlowServerTest/https1/");
+            https1Server.start();
+            https1URI = new URI("https://" + https1Server.serverAuthority() + "/HttpSlowServerTest/https1/");
+
+            // HTTP/2.0
+            http2Server = HttpTestServer.of(
+                    new Http2TestServer("localhost", false, 0));
+            http2Server.addHandler(new HttpTestSlowHandler(), "/HttpSlowServerTest/http2/");
+            http2Server.start();
+            http2URI = new URI("http://" + http2Server.serverAuthority() + "/HttpSlowServerTest/http2/");
+
+            // HTTPS/2.0
+            https2Server = HttpTestServer.of(
+                    new Http2TestServer("localhost", true, 0));
+            https2Server.addHandler(new HttpTestSlowHandler(), "/HttpSlowServerTest/https2/");
+            https2Server.start();
+            https2URI = new URI("https://" + https2Server.serverAuthority() + "/HttpSlowServerTest/https2/");
+
+            proxy = DigestEchoServer.createHttpsProxyTunnel(
+                    DigestEchoServer.HttpAuthSchemeType.NONE);
+            proxyAddress = proxy.getProxyAddress();
+            proxySelector = new HttpProxySelector(proxyAddress);
+            client = newHttpClient(proxySelector);
+            System.out.println("Setup: done");
+        } catch (Exception x) {
+            tearDown(); throw x;
+        } catch (Error e) {
+            tearDown(); throw e;
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        HttpSlowServerTest test = new HttpSlowServerTest();
+        test.setUp();
+        long start = System.nanoTime();
+        try {
+            test.run(args);
+        } finally {
+            try {
+                long elapsed = System.nanoTime() - start;
+                System.out.println("*** Elapsed: " + Duration.ofNanos(elapsed));
+            } finally {
+                test.tearDown();
+            }
+        }
+    }
+
+    public void run(String... args) throws Exception {
+        List<URI> serverURIs = List.of(http1URI, http2URI, https1URI, https2URI);
+        for (int i=0; i<20; i++) {
+            for (URI base : serverURIs) {
+                if (base.getScheme().equalsIgnoreCase("https")) {
+                    URI proxy = i % 1 == 0 ? base.resolve(URI.create("proxy/foo?n="+requestCounter.incrementAndGet()))
+                    : base.resolve(URI.create("direct/foo?n="+requestCounter.incrementAndGet()));
+                    test(proxy);
+                }
+            }
+            for (URI base : serverURIs) {
+                URI direct = base.resolve(URI.create("direct/foo?n="+requestCounter.incrementAndGet()));
+                test(direct);
+            }
+        }
+        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
+    }
+
+    public void test(URI uri) throws Exception {
+        System.out.println("Testing with " + uri);
+        pending.add(uri);
+        HttpRequest request = HttpRequest.newBuilder(uri).build();
+        CompletableFuture<HttpResponse<String>> resp =
+                client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
+                .whenComplete((r, t) -> this.requestCompleted(request, r, t));
+        futures.add(resp);
+    }
+
+    private void requestCompleted(HttpRequest request, HttpResponse<?> r, Throwable t) {
+        responseCounter.incrementAndGet();
+        pending.remove(request.uri());
+        System.out.println(request + " -> " + (t == null ? r : t)
+                + " [still pending: " + (requestCounter.get() - responseCounter.get()) +"]");
+        if (pending.size() < 5 && requestCounter.get() > 100) {
+            pending.forEach(u -> System.out.println("\tpending: " + u));
+        }
+    }
+
+    public void tearDown() {
+        proxy = stop(proxy, DigestEchoServer.TunnelingProxy::stop);
+        http1Server = stop(http1Server, HttpTestServer::stop);
+        https1Server = stop(https1Server, HttpTestServer::stop);
+        http2Server = stop(http2Server, HttpTestServer::stop);
+        https2Server = stop(https2Server, HttpTestServer::stop);
+        client = null;
+        try {
+            executor.awaitTermination(2000, TimeUnit.MILLISECONDS);
+        } catch (Throwable x) {
+        } finally {
+            executor.shutdownNow();
+        }
+        try {
+            clientexec.awaitTermination(2000, TimeUnit.MILLISECONDS);
+        } catch (Throwable x) {
+        } finally {
+            clientexec.shutdownNow();
+        }
+        System.out.println("Teardown: done");
+    }
+
+    private interface Stoppable<T> { public void stop(T service) throws Exception; }
+
+    static <T>  T stop(T service, Stoppable<T> stop) {
+        try { if (service != null) stop.stop(service); } catch (Throwable x) { };
+        return null;
+    }
+
+    static class HttpProxySelector extends ProxySelector {
+        private static final List<Proxy> NO_PROXY = List.of(Proxy.NO_PROXY);
+        private final List<Proxy> proxyList;
+        HttpProxySelector(InetSocketAddress proxyAddress) {
+            proxyList = List.of(new Proxy(Proxy.Type.HTTP, proxyAddress));
+        }
+
+        @Override
+        public List<Proxy> select(URI uri) {
+            // our proxy only supports tunneling
+            if (uri.getScheme().equalsIgnoreCase("https")) {
+                if (uri.getPath().contains("/proxy/")) {
+                    return proxyList;
+                }
+            }
+            return NO_PROXY;
+        }
+
+        @Override
+        public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+            System.err.println("Connection to proxy failed: " + ioe);
+            System.err.println("Proxy: " + sa);
+            System.err.println("\tURI: " + uri);
+            ioe.printStackTrace();
+        }
+    }
+
+    public static class HttpTestSlowHandler implements HttpTestHandler {
+        static final AtomicLong respCounter = new AtomicLong();
+        @Override
+        public void handle(HttpTestExchange t) throws IOException {
+            try (InputStream is = t.getRequestBody();
+                 OutputStream os = t.getResponseBody()) {
+                byte[] bytes = is.readAllBytes();
+                assert bytes.length == 0;
+                URI u = t.getRequestURI();
+                long responseID = Long.parseLong(u.getQuery().substring(2));
+                System.out.println("Server " + t.getRequestURI() + " sending response " + responseID);
+                t.sendResponseHeaders(200, -1);
+                for (String part : data) {
+                    bytes = part.getBytes(StandardCharsets.UTF_8);
+                    os.write(bytes);
+                    os.flush();
+                    System.out.println("\tresp:" + responseID + ": wrote " + bytes.length + " bytes");
+                    // wait...
+                    try { Thread.sleep(300); } catch (InterruptedException x) {};
+                }
+                System.out.println("\tresp:" + responseID + ": done");
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/LargeHandshakeTest.java	Wed Oct 16 14:50:53 2019 +0100
@@ -0,0 +1,1200 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsServer;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.time.Duration;
+import java.util.Base64;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @test
+ * @bug 8231449
+ * @summary This test verifies that the HttpClient works correctly when the server
+ *          sends a large certificate. This test will not pass without
+ *          the fix for JDK-8231449. To regenerate the certificate, modify the
+ *          COMMAND constant as you need, possibly changing the start date
+ *          and validity of the certificate in the command, then run the test.
+ *          The test will run with the old certificate, but will print the new command.
+ *          Copy paste the new command printed by this test into a terminal.
+ *          Then modify the at run line to pass the file generated by that command
+ *          as first argument, and copy paste the new values of the COMMAND and
+ *          BASE64_CERT constant printed by the test into the test.
+ *          Then restore the original at run line and test again.
+ * @library /test/lib http2/server
+ * @build jdk.test.lib.net.SimpleSSLContext HttpServerAdapters DigestEchoServer LargeHandshakeTest
+ * @modules java.net.http/jdk.internal.net.http.common
+ *          java.net.http/jdk.internal.net.http.frame
+ *          java.net.http/jdk.internal.net.http.hpack
+ *          java.logging
+ *          java.base/sun.net.www.http
+ *          java.base/sun.net.www
+ *          java.base/sun.net
+ * @run main/othervm -Dtest.requiresHost=true
+ *                   -Djdk.httpclient.HttpClient.log=headers
+ *                   -Djdk.internal.httpclient.debug=true
+ *                   LargeHandshakeTest
+ *
+ */
+public class LargeHandshakeTest implements HttpServerAdapters {
+
+    // Use this command to regenerate the keystore file whose content is
+    // base 64 encoded into this file (close your eyes):
+    private static final String COMMAND =
+                    "keytool -genkeypair -keyalg RSA -startdate 2019/09/30 -valid" +
+                    "ity 13000 -keysize 1024 -dname \"C=Duke, ST=CA-State, L=CA-Ci" +
+                    "ty, O=CA-Org\" -deststoretype PKCS12 -alias server -keystore " +
+                    "temp0.jks -storepass passphrase -ext san:critical=dns:localh" +
+                    "ost,ip:127.0.0.1,ip:0:0:0:0:0:0:0:1,uri:http://www.example.c" +
+                    "om/1.2.3.6.1.4.1.11129.666.666.666.999/041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100,uri:http://www.example.com/1.2.3.6.1.4.1.111" +
+                    "29.666.666.666.999.2/041287234567896776987654327821000412872" +
+                    "345678967769876543278210004128723456789677698765432782100041" +
+                    "287234567896776987654327821000412872345678967769876543278210" +
+                    "004128723456789677698765432782100041287234567896776987654327" +
+                    "821000412872345678967769876543278210004128723456789677698765" +
+                    "432782100041287234567896776987654327821000412872345678967769" +
+                    "876543278210004128723456789677698765432782100041287234567896" +
+                    "776987654327821000412872345678967769876543278210004128723456" +
+                    "789677698765432782100041287234567896776987654327821000412872" +
+                    "345678967769876543278210004128723456789677698765432782100041" +
+                    "287234567896776987654327821000412872345678967769876543278210" +
+                    "004128723456789677698765432782100041287234567896776987654327" +
+                    "821000412872345678967769876543278210004128723456789677698765" +
+                    "432782100041287234567896776987654327821000412872345678967769" +
+                    "876543278210004128723456789677698765432782100041287234567896" +
+                    "776987654327821000412872345678967769876543278210004128723456" +
+                    "789677698765432782100041287234567896776987654327821000412872" +
+                    "345678967769876543278210004128723456789677698765432782100041" +
+                    "287234567896776987654327821000412872345678967769876543278210" +
+                    "004128723456789677698765432782100041287234567896776987654327" +
+                    "821000412872345678967769876543278210004128723456789677698765" +
+                    "432782100041287234567896776987654327821000412872345678967769" +
+                    "876543278210004128723456789677698765432782100041287234567896" +
+                    "776987654327821000412872345678967769876543278210004128723456" +
+                    "789677698765432782100041287234567896776987654327821000412872" +
+                    "345678967769876543278210004128723456789677698765432782100,ur" +
+                    "i:http://www.example.com/1.2.3.6.1.4.1.11129.666.666.666.999" +
+                    ".2/041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "27821000412872345678967769876543278210001,uri:http://www.exa" +
+                    "mple.com/1.2.3.6.1.4.1.11129.666.666.666.999.2/0412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "5678967769876543278210002,uri:http://www.example.com/1.2.3.6" +
+                    ".1.4.1.11129.666.666.666.999.2/04128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210003,uri:http://www.example.com/1.2.3.6.1.4.1.11129.666" +
+                    ".666.666.999.2/041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "96776987654327821000412872345678967769876543278210004,uri:ht" +
+                    "tp://www.example.com/1.2.3.6.1.4.1.11129.666.666.666.999.2/0" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "1000412872345678967769876543278210005,uri:http://www.example" +
+                    ".com/1.2.3.6.1.4.1.11129.666.666.666.999.2/04128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210006,uri:http://www.example.com/1.2.3.6.1.4" +
+                    ".1.11129.666.666.666.999.2/041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "10007,uri:http://www.example.com/1.2.3.6.1.4.1.11129.666.666" +
+                    ".666.999.2/0412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "6987654327821000412872345678967769876543278210008,uri:http:/" +
+                    "/www.example.com/1.2.3.6.1.4.1.11129.666.666.666.999.2/04128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210009,uri:http://www.example.com" +
+                    "/1.2.3.6.1.4.1.11129.666.666.666.999.2/041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "698765432782100041287234567896776987654327821000412872345678" +
+                    "967769876543278210004128723456789677698765432782100041287234" +
+                    "567896776987654327821000412872345678967769876543278210004128" +
+                    "723456789677698765432782100041287234567896776987654327821000" +
+                    "412872345678967769876543278210004128723456789677698765432782" +
+                    "100041287234567896776987654327821000412872345678967769876543" +
+                    "278210004128723456789677698765432782100041287234567896776987" +
+                    "654327821000412872345678967769876543278210004128723456789677" +
+                    "6987654327821000A";
+
+    // This is a Base64 encoded keystore containing our certificate.
+    // The keystore itself was produced with the command above, then its content
+    // base 64 encoded into the string below. The helper function to produce
+    // and format the string below are included in this file.
+    private static final String BASE64_CERT =
+                    "MIJR0AIBAzCCUYoGCSqGSIb3DQEHAaCCUXsEglF3MIJRczCCAyAGCSqGSIb3" +
+                    "DQEHAaCCAxEEggMNMIIDCTCCAwUGCyqGSIb3DQEMCgECoIICsjCCAq4wKAYK" +
+                    "KoZIhvcNAQwBAzAaBBSx1wdTxGqb9z4exOHVZNswvFL+oQICBAAEggKA7JdM" +
+                    "91kkP9QkG/igw2p+prxeEOQSmyScKMLtln81eKvT9zpvNjtT+hjABcH2QY8u" +
+                    "1Z3Ji48Umoaxi38Fk58/VazFM6wpL47VVNJ2EeTdj8sFoo8ExCH8EHJNNaVK" +
+                    "VNTG0YWOMa/HOPttl5wtD6pReGNOrVYVOnI2aY6zTqwI0sZS4uPczfb21vyI" +
+                    "NyF4B0Z9WGl77PRoGwrSeoLspISBTq6/JE8UhMWtuz7xnXw04DGp4DeIOO9n" +
+                    "E8+VBRKOELPqNaQ+VEgnwPNtPzjohi4Cwaf84c6vokAl1S/V6GzS0Al1mSGH" +
+                    "syAaszDYWcXXp2JpSXVAztySWZErwHE49/P42taXdhJvOSfqYb6FHpdrCXST" +
+                    "TPo+ULCGxQ83EGfnb/qaqAYZrS//+lzzqw18OY0JcF1i+cGHY8ofJK+bYr7x" +
+                    "ZyC8pLut84pEWNTp1V7SQcMif2Gd2SO2Y+ua4isjfMLNeNE/4puCV1vYsyiz" +
+                    "C9Gnp0Jywv13ioaC24Qy68uVQ81TvwizN3j7FxPCQOEEjXpfJ+5x2q0pUfqp" +
+                    "Roy7ow3Z+d+/fpMIcgyMqWidzLBChRkx4Ugnh7rYBfY1ghchlu8WIIhiR/8p" +
+                    "EBX5WQHyEtwrXOFiWxT+QwXWjbs9dQSUoCU1i2zwCFW9R8FkY2yb98QxF74z" +
+                    "0TpyW+w6cGPUNUd2T143PL4eGt4rGUBUMewe2ENSgZCDstvtiNPfccW8f9tq" +
+                    "G49pHBZt1ZIadM/DbCk1cqDD3u2/e7c57mInFkBBJKjl2K7GK9EYsiey+3Fk" +
+                    "NvkxbaF+89OTqEDPP4E97EeHkk/MFe0bQ/a/aXZrTPSN7mNgusWBQyztnYex" +
+                    "BRr8sPRhNDFAMBsGCSqGSIb3DQEJFDEOHgwAcwBlAHIAdgBlAHIwIQYJKoZI" +
+                    "hvcNAQkVMRQEElRpbWUgMTU2OTk1MzczODEyNzCCTksGCSqGSIb3DQEHBqCC" +
+                    "Tjwwgk44AgEAMIJOMQYJKoZIhvcNAQcBMCgGCiqGSIb3DQEMAQYwGgQUBIoZ" +
+                    "0r5Kc2cs6fHseA1vKWpQi30CAgQAgIJN+Lch8gN0kyMcpdNDM2iAfBHd/kXZ" +
+                    "4ye8lmGvvV0Yy9dd4Q6zmOmkPjWcusyh/vJdya8OG8Fxc/nQmyhB441qtJR5" +
+                    "dwIQe/7lft2gDg2sBD0osPvEHesvFVr0+2cy22sdBXS4ihdtVTciPV+4v0EU" +
+                    "AK6Lcib57Ml7MI6VhBGpWLkaZXv/25CqXGaiY85dSXPHMugvfJn8JoNxe+tN" +
+                    "PDEAy5ar5bnSD9Xl8GyQwHFwQ1P+gEjwg0+hC6OQA6Eg0jBFhIL+dCf6FJwz" +
+                    "J2HekpKXcyCTNTx0HB8AW3u4gVjao0B7oNr0GZprpPgYp4dl04Tar6fmb9MN" +
+                    "w4MWkcySjX0wCNjpvE0bWlUx55jlN+25SWNEafmbzRNECW1hT6Xl3HLLNQQv" +
+                    "8fIgkd2200+Ppwk+cG2dYMrl0MpHPxOo2l+nLNDI+a84v6R2Mqf3qVNfcRxZ" +
+                    "Xmw0kJ1JNUKhd0Dy6FKB7tY1Gwc4rfQQgdUXTlnB96rUrSK2o91QZZ9rN3PD" +
+                    "SbbQRzSBSPMDen+W5878kwd1BkDH1ao7qOHoGYYiYxczkMVncOtOIdg8VScU" +
+                    "t/N1NS9Yrzlj0aAYwX5EYmYzFMiyXr+El/7VnrLHBiaYwJaYVw7a1HgkSXxb" +
+                    "er/vgM639AIhT5ab9NFF+ib+7qwbWPzggIBtfm1bXiRPE6Ue0+aQ9g4vYSR7" +
+                    "QNusu/2Kwyrd6cwqJnQnG53wiSXh3hs7dvNRYpM16iF1dTeud0FSpQx1tMFi" +
+                    "Wp9cGwtUSzvLw1+5Mvz7igLTNAPyTMFNAYFR7JZgRjg1035Y7Xo4aXNMVtGp" +
+                    "BCa+/eJzE33CHH2+kuSa13N6qHA2Ek33aR4Vi7P/QOI1aghsR6tL62ctoq4c" +
+                    "FmRwvReYGdFK08+B2auPHOEPdGaPefydmPNnFLDguH6d2NYIH8cgzpjz48Gf" +
+                    "i5UQfh/UJAxricLjuV2eor7gZCCaC9MfnLlHnDH5MYUQgDsGBTRxh6rpZxVo" +
+                    "PFUm1DQCFdDHgmvVz+GAuiVzUu0TrUAxBeycl9lrDshgZj2jd9FU7XcMFwzC" +
+                    "dMsWJaA8EC8u31vnpWWqK8C03pdXTbWOEJdIDMXKOzd2ZxjGPnBSNXNRsGzi" +
+                    "RdEspHElrfGwHA5Vpsj4Q6tMZ8tZ+gSSmBrKP6xNUDNwi1bPayJw/dAKaC+D" +
+                    "vp5sx/+nZcxSF/ig8ZM6aA3escS1GBNBWFrx2vEASHNpHZsTIOTuOM4e584/" +
+                    "NJpNlRfaTzJ3i9/XoufnHbT5pmpZgxlZckYto3h6lL8R0bXthICRrI37Oh6s" +
+                    "yfO7zhMiGoNdaFAnXTsMzA+Uwr1gesHWE9Rbd+jrkcGgL4Zst/A/c64F58qt" +
+                    "J2RzA3xJCGQ6AXB3SlDHLObuYZ48TH4v2nJ4S8RTs/ant/T9DTRJyhQMa9+P" +
+                    "QC8Ny1ejFOK71Oqjz7J2jGpoBm3gDRZDYeBa6ZiMeJf8Q+bkpbEgUQbdiXhn" +
+                    "dpN7acdJTSHnO5/3Y4G1T6kLNzKc1+NYiYwH9Y6KIMa9IaocOA4wCHEFog7Z" +
+                    "Ac6vFv6r9/cnXEIe9/t5gYLL7X/gU6HMqZFjM0QjRru/Sy9vYfpytqrnOf/q" +
+                    "eOTJ9gRdIn7jNVTrgrE3ZgNvxYapaDMyFG/ixZV0cblsVJ6JO4MArOQq6q9/" +
+                    "LWGtYYEDclyLNxUTYf0gHmVRhYV8rlyMKXtDu0aBOOn13dRxKKVN5pG+LrKe" +
+                    "V+FZ3tDhYiFBxI/gkAiegSQlvknJlbEuanCWFq7sHOZ3C76L5G4qtRv7tHAs" +
+                    "8rtqzhYnddxIkTcj4tkVYsFxkj1afSuLSSoYqc0jYZUXHkL+5U0ZRxhJbo6b" +
+                    "/QVtiGXbS9i1em3+Yr47jHRH7f5Crlc2EdEzZaIm8tCw5G3CddXhhCpQTT3r" +
+                    "Mjwep5/w2ai8qbGC1xo6AV7ZyQS535UbIhOYMPO7vd3oUBmQXyutSxx8Wedx" +
+                    "HbIlBdZ44JlRWELdL6Ejo7PwhG0F3Zd3+FtasM13teYlbDWdFeoTF+inrXCG" +
+                    "BCSqoUDYLAAxRK7oYKSDqfrcRioHO4AkeaF8x5YZpRSM73yawZaWeLVq1NKb" +
+                    "DdeSJEjqDs5Ve6cA/jSLDtlJqudspFamzkdUWa4L1Tc15JY2YD8urNz/qXUK" +
+                    "E74H2MtQWjrQ8Yjz7y+3yUTYd8Clp1dBKV2haIr+sCTnFtUXaQ9LZF5S+enU" +
+                    "mOf7OmdFObwvV1f43HDuDSiFZpE+w57YmAgSIfWelKnGvvn1lBBWBomyVBKo" +
+                    "69seibaSyUXyuq8q3525ZKpHIcic7doYOYgqU94HBQOwkwTtoiX3tKX4EgDa" +
+                    "9AjYD4LHITkwgKzsQAe+3ASKq3SPFXi/UbYuZNXdkXgh7kqL6PZPD6ayIxOW" +
+                    "f8E95fZRXPyNbKf5U9pp9hVKeuOPKbEYKsQNG0ZiephMmV3S6JjSqLo+qTU+" +
+                    "QyGH3vhwlhda9cmxrpKCy0KM8AHyWoS4L5TAiJXp6moCttP0v8P/RRFtBePl" +
+                    "53nyLwZ5SyXv4ifVayJyyeDXn08In9MINLRyjvNF2gHbHI7XbeQKqB3yIBC9" +
+                    "sKpyShX6UKb1Dnd3Z19rT4a7QUkqfcodX9cU7tV6ncRuP5ZJRLHQQi9w/2DI" +
+                    "Leu8Y43jJ/CWYo8Fb8IXHrWA/hwNT28Yr3OcZUIDFdbdxenTKyhMlDOQ87aU" +
+                    "VXBCnq96jEAzY0NY3PZj+CBvts2wlCJJDhBCVTXM9OusZjH2/B6phWO7ybMq" +
+                    "15QqYyypNM62oBlu82psklHRREO1D23MaOyoXSHYVFxvLMKxhstK3wd59cst" +
+                    "8V/uvzzrURJu85X1X0cQJaKB+OjvBT8n8e9jzjATOTiGH2ULLSro2wc7Ne1r" +
+                    "bEK4qHeuSwlvulvcqB7wK84PItmVOW+g9VuXA9QGGHxzybbs+/QaMqJzdN6C" +
+                    "oS1Az5UmvZbdnSAuXtW8xiKJ+ELAGCjOcdkzQaAbd8Tq5hUXAgYeNQnoDjJz" +
+                    "+a8CTk1IaGkeHlg7MrV98AuIQjUB16IU6gE4MH6ueOIZhF3PB2Fp0dGPXL6O" +
+                    "KzIiC3jM7rIuTmOstI/t+XB3+BYxKvLuQhbV5n8sUBsGFFGBKoxIZawKG1Pk" +
+                    "3Dny2rzcB2JkcOO8r6f0dA5V4PKIrhmVeQJyHgY+/fF+JMZxfjYmaSrwu/1R" +
+                    "JnlDfDCIwNon7smi8QwXbEvRehI5a883yI07COM1paHBKNddlw4sfkfbIrNx" +
+                    "LQJLb0mHgqvHAblivBmMuaiZaK0MVOgZTe4bYuYdRSN8/ueZDHOo7lyfXGzc" +
+                    "8FEThaiGp5m/uWYJgG2FIRxM6by9PhusaTZgJ8DmjRLF5yDuR14gau6zj7GZ" +
+                    "TMXGDIDTRWTKTP0/DYF+EEnuWJb2JrrxMnWH8X7xXhrRA6Ku3nqVEwy8eUnR" +
+                    "WtLAQkApLOEhsp3wQyLRbaydcD1Si9s5JICy/m5Sv2NXseKbmAHDphTYuSGr" +
+                    "Gjk5ryTEaaiULVvi7mv1HqDq3fli/PAzMIcsK0yaF9rQv7JjtsOtZtr77+zs" +
+                    "vM8CSY835WpqTtjGDC+HWHulEADgS4ShpIrn3zgyEd1jrRY6rR5ZDuioc7sE" +
+                    "2A/8w6I5KkmcXMu/jKoLjotdUvvGMMAIhqCZD8sD+F2jpNcCV3/7NpwaJSk+" +
+                    "61rfPxzQRKu1jn/9TV7e5PyWQMkPOMbTMlA9jJPi5WEXNanIFTJBYgI6vmYc" +
+                    "156uLrlXh4F5gpLN42JyhddSbV4dXXLt8an9X4dMAJmdgBmzFldFPU7I88Xm" +
+                    "nuy3Rkqs2p4eb44b4oe9xdIyUw3fkfdoYzRx96X2Tvlx67BvbprglcdoiBce" +
+                    "UitoMsN5tI9+o4/wp6SCU1nv97Yl/kGR6xyY/5irK3wVr2DsrRSF/WuycPFV" +
+                    "3EOZLCoh/Y+9yH2dLlslarL2ZVIe701NTp/fN1GCQoCI2elSwXZAiJ446Fyv" +
+                    "F5x8UTIbAZKiJfL8F09XVnNSRIp78rcNUUeFKehewGJ/I6WtD/SyGc7uTCKi" +
+                    "EqXAwoQN+4GcncLQ2eqFFqR1aZLEcJKU79EpKu08rFTl0X9NaA7Qd0nXAiNF" +
+                    "EUz1Xo33ReE9l67+QLXYK17UIqxkFQnawydZKt1T7HeRpTKEQzo9/wLk+IYD" +
+                    "m5y8DXHK/d/kPySyLW4+srw3HRIe2Cuz9nNhPTUKtVC9CPt9NT9guhOhDGRA" +
+                    "XBkF08Snjpn7wG6G1cE2gwU3W4oQIvCAwpdsJ8leg0fbosc1UD1QHR+3CxIU" +
+                    "0yoPm19LygYjIyTg55bGBID0GV+DAHKUVHIUkBpaPM0PcSxsJgFv1seuQSJy" +
+                    "dRUcKGzPg+xJhVvHuuAnnlBibjzrl/DBLCPgOl2kybpsv1sLJdTLEkwkv/yf" +
+                    "83PdSz+uPha5hv7AGfxEaaUwbnktHultVbjO+IKBNqgjz763XgUAKbtfxW4u" +
+                    "VLBESpZiVe/gzgNV7j+6+vHWsOC8GBMjJwHGAFfTyjYv9BmrxkQyabZ8wCb/" +
+                    "5NPvaHBH+qkWVVWBaLZuOkgG9dmQI/oEcO2H9IDtve3OEXGuP2zG3xkk7h8W" +
+                    "RwcHoz/anAbEHA1wmPYn/NFn2FZW3KxdyD0/URj4DaZqMYCJMCN8TkbLBxQk" +
+                    "ZS+8VCcXMALybXUD9OLX/jUHaPqO70e9+o4cD+O/JfxKkTj5A2WD2345b8nK" +
+                    "Lill7lJ5JlYekkGG4LQf92FbH0ytLSVB+A4oc7/nxI5ciWy5vDmaG+3HS5W2" +
+                    "mGxnpLApVJZhhiJRB5fjfgRiuVbcGNWFQtgH2imMorrE0FzONSQfepLtBSQM" +
+                    "Ec8NWgmEa5O0RIYLMblXisxt7jB0k8NqgiSm3dmNmR7GhbBt+mg95uCXWmB7" +
+                    "nbcUaTWUw4Lb1EwVB/MmUEBjer/JYDADWz+NS/0VcAgBbjk7vrjAdgXkgiRB" +
+                    "l9qBDioO73qjmjeTiCjoLsuEreXPu9WTaZDuyKO1uxHoesZE0AcWjcZgKVY+" +
+                    "1Eqz3awCUxQTH+/1HsVxONmbYzJgMZ3f9Kuk7dxwFc0jgIw0LfFNnBDW3wY6" +
+                    "nrdtm7vEuR0gfYU83oqXQqAMQGwMZXKXtibS7yPL+rinLwcoWYkZaIaI6LTb" +
+                    "3DthepCtsYfIpaErOHMVOqxDUb4n5e0EUJiy7gw9JQjDX+VzpxAnt2Kc6/fl" +
+                    "kon4xadP5oyNx3YU+jIywk8p/NMhDfia0fFvl2BHRcgFXHyOa8IPNmRb0CgO" +
+                    "78Zbt3NG2+cFUx1WwOuCohfoYt/STUnxDXpOCrxYk2CD09Vm+2xC+7+VLfex" +
+                    "UnKzKH0tWLNyzA5XKSHyLe6lJCO+mvgdp2vrO7i053YVfqfqWSL9rhR0tH5w" +
+                    "NO8uUd1/ozkIjb74PSft0txTP25/c4/MEWy2JIg+HMM+2fmanJi4xJsuxBfq" +
+                    "2p/7AYfmsp05TfoSLieWDBNx3lrADGUnuPf/o8Zo2tgn8ek4ig706kqzZy9W" +
+                    "p1PaYNK6p/MLI86Sv6OLHf6fR0u6IsGPqMcYx9J7U5SCemntLvucfWRAqRn4" +
+                    "5htDzdTFrtO2nXLNDxk20DQtol7yyBg3ngVX8XDhKReGdV/Z9kEeJhgzbxLT" +
+                    "fbjoGQD5twirxYyV+eRqQZM7fu71Srg6lz7najEyH9pbQjpEPForDy5Briry" +
+                    "nplWImdWd+KwG0N35+H48kpxAU09sOUGURzsUPdwTANQUnrWWMo5+UIjxXtR" +
+                    "cDMuQmj8vlYH8iH0bKgexZMb7cRciRUF3az3lo24FA6l7e4SkyFErGJITGFN" +
+                    "IHO7wDz9h4rLikjGkz4G1d04XQCxrpHSrW9fj808up1LSoaye5ijutJihEHu" +
+                    "hkiFLi1nwuhd4ZrAZD+0VkTde9GAi9xPa8Cx+lh4JMB5mghk8uTjv7G4KqzE" +
+                    "Wlpos30CMyCc5cBGqkCVLKt64+KgUyt0FnU6tbZve7i1/oYyUaWx7BCk6o9f" +
+                    "9PuQM2bOwqhjRtpBR7q9Zb9H+qytAM8psLV6Sh9K0cK5Ug4BBEAtCK+JHBHG" +
+                    "U3zkhu2FXlm0Mnp5xiaTvPwPWe0P/mrD4Fhgn2pbPvyJXTPgWV107zafKuLW" +
+                    "34/+ML8oog69/oUOSAw4vaJx8PWguMRoQIsS6ATT2RhUVn/t21fJK9j6eF/B" +
+                    "YG5IIEavyybQMILx86+SI5sLi9j+Xqd5bPLWnZTataP+voMTzyyYqAi6ybEl" +
+                    "4QkWM0c8t141t7FULXHMTrKhdZDocb9plz3sfYk6cEqlOphvmhbZbT88iWJe" +
+                    "ndBHJxY0SL0NQ7Yl6fg/DHt4goXSIWGL5IamBM8CZnVwyCHz7laP15Zsc92H" +
+                    "DqWgbojeMxiPSVnFqxFzL/6TBBTQYuNl6+CgCOPIe88FwhZvfwzXVnQHb91i" +
+                    "58t7pwXCURscYFK7iyxi7QTrofT0QM4upsI3zBqOO0X2SOW5H6Dx3uukNP2r" +
+                    "Ud0AbXWHglnooaMNl/XRfYyb6VYg28r7qcfsSKlWuRenNf/ejaLl6GeO/ef9" +
+                    "VI+ia0qv7S2Hi6xgdw3/NTqltehTyZylC7LtKr/TVRFdGLsFKtGvT/KGMYt4" +
+                    "eUYR1zz61CVxAjjGAg+NlpTG8P50xralMthUppA7P6X4j4cDrD3E74HYg9Zv" +
+                    "4PLAh6MOymj0YbW+/QjbO13DeSXxYX5/EGk78+sBOVSzvigI9NLvWHZMFylp" +
+                    "mi5m3Wh7cQojpkeKXSw0XRd8lSeMN6lrzkMknniWfZZhK3idGCH3kHd7IGzC" +
+                    "X67K3gf+8syXOIpoDfUJHVpfvKZCw5XN2huYNGKP8KqELMoDatmEoB4hoVq/" +
+                    "VIssuwanWj3vDOxp7bYRUXID9dfTrgq5A5+1RuzrJVpmK+maTGypBDvemJhw" +
+                    "OSP62Oa+1ryH3+e1yqWDbWJrDts1S1wqMRRG5LKP16e3ZUYo33Gir/qott4b" +
+                    "J3PSvZn50KLnkyt1apUGE3pIyytvencsCca26qm+EGLYJXu+njUHIU+s4z5Y" +
+                    "xxrR4xcqc4CKXKK5+z34YCkdGM8JME8fCinDgrsixnYCjyv367LrQ+/YRvtF" +
+                    "6gF2XrCyPNTtRisqHg8Ug3CSKMPYJBXO2uu8/9dxF+r8LGv0pbVoPUBJpPiL" +
+                    "+tnxY0ggyOiU2zsUfb3QcuHONXU/2qv9FxUzBZDUDVuhXeCgNDtfv5QN7R2S" +
+                    "VoWWQDcQP5Vop61TXUhLnNaU6LwaXsVIIi9hbv4k7LV3tOxgjtyddymxch0x" +
+                    "4mR6XCXVwPx3yVIEwju7ANKsDxR+yT7crRYpPstka9lY8Y73w5MIgz2LVk65" +
+                    "xJ13puMbAGnCuSEuQgRdPHyt9JDp8KfvQ0/FegDE1axheKVAfCKgCuAudIxZ" +
+                    "UGXjPXLh4hMp+o87hw0Mkr0tfmKBd3KzRkjZm854ksnURKPODIjEqhHcW4t3" +
+                    "gRQ3cYiMqcv0cDd7cDw/4dFsfJs11aXSF674f1lhOjYqB2Xa7EMDQxJC2zw9" +
+                    "6HPVKHmGGSNAv/UiJHjcQuJslGVq1SisnSWgpMND5+QnxHDBww1p7DqXpDQJ" +
+                    "boHjZKPM/gi9N4GD6iHGbF4l8Mx1YzuzoAbqqg/6v+fmYQRUgzGnNRHW781F" +
+                    "R6J6R6JwNOVTaSvlDFzQukHdwpyqcR4OM6XkaxNK3SMRWye4O1/U+12FhPYx" +
+                    "5pPHMId8d8voJIIMPYRjFIkZAWpYbavjtV5x4xUWu/Ch6SeZ6uQu5h3WbFVe" +
+                    "buXjnOQbVIixTh8qo1WXXKiG9DUYHowgz2XWC45izfOnt1xwWj1He0IQoYWg" +
+                    "WkePdJPKfS5igRdzjwEp29RlLoqXj9TsICzYhWY626A3UjnTSOAg8Av2iWxx" +
+                    "j+3I3zA5I34udiSf/4iyrWZHeaLP3S4wIDrTTNzOh18NvspyLJrjoRokXwoJ" +
+                    "g5BnQan1rHl4p2e7oNLPhc9ewKF0QWdi2rJZDY6qO7sa6jt2C57g8jAYhAFS" +
+                    "2cXjJNQAPCgoodeKTnJ/1R6Ykk+byXzeDc1NuHPHiNbtGu54Hz5Xd3P8hYJ3" +
+                    "VBYvhkF90Rq7LvBBXyVTy8tL0I0N9BdCQrnd94HPm+fMj/nwc0pqCi9b68ex" +
+                    "Gcr8YM3vSg9xRHEBXsWjL16oKchvwavauC2Uap/OEizdkQUKBZlzDazbXTN6" +
+                    "dZpCyFcB+Ox/zgltv3jxum19WdgjyIddqBIyiKxFtC3XunK5yvmr/fFZV8X3" +
+                    "vxbxV6y0QPqJDM2ctb/ndiYaGn60EJQjxETGIanp5szgKFBYs1p1mjyov5p6" +
+                    "p76yEvTm+Ba+LX4ngM1AddqyfbPYscxtIyhUsqDqQ7vSNHQezTBcz63OclbJ" +
+                    "G2SyQFZfZKIgUXiZt+ZC3BwKRBGQZUE+UIV9WjrIvhtZN7A1qdo42c0S/skz" +
+                    "lHMKR75/PxVVXRaArCRytERwdLOvlyBE1xl3rxnxErTQHViP0xEzOfOpQ2M2" +
+                    "Ds9TcXkm66ZRnJBuk+fp4m8iiz7IfRlrI8y4AUuI9LEaFOKkh0HH7sjSWzs5" +
+                    "7uGe0eBZsLpzWoDd2Uacht9+xLcnn8tQQ32H0KHkZjm1UGtbSyUSvvoC8SQk" +
+                    "U8QstWumKzrzf3/GYuW43N4TIBUvl7GWcpmpevuAicl6SVeDjyaSGp1n/OUM" +
+                    "FH6e6kSgyDvrs/V/pQran+Dymby9DPV7fUWNo51rFdqvLhktICGGWRuUXjvA" +
+                    "eOOk4JhsD4MSlKysuGfbcUhFnkhjFkctA63HEAPAKNLoQze2tRwG+tZhJuAp" +
+                    "Q+3YEHce9DEVo/13eYVKRu4yFqD0G+KAkwcXHcFwH4b8ByTJ6K4BboRIpGAI" +
+                    "sCWN2r4Yx6sH7hDgDH/ywTs3xTI+JBDGk4+15EXUSVA41bCKEsT3BiksVE7b" +
+                    "Uo2eXPFkG7ikOuyMr5xfWtIN5v3tg/lE8K45LtWgOT2mZeEZVEVmgozGwR55" +
+                    "OqZvFeqdZbV1l57N2vSf3YkJFU8CRwd6uDZ96C/Fax7aL4biwSragaXrYu04" +
+                    "XqqWIJVYOZQnHHHTGSH+C8+NEZJiAEH2ILRlqa8VCTGPTVd96+tVriytRGFE" +
+                    "wG2xl4jYviismG73MqzuCq6iwx+HaWTiGSlXzMfhKL0DE3rhPmWrKIjdR+Ub" +
+                    "Lp1C/L2mM/y+DEMoj59/l+SwMaijQY6oUpOtVGS+Pm2C03JFNSFZyYo+HHAC" +
+                    "OkBnWMUKCGWMWOejGaC4vtBxZsHn7Q5ij+9diNfxyLEWc08L/muJG46bzlHY" +
+                    "+t6W/j4UdYuizHNm1og+DD17Kbxk7fjRsQr1ARKeDkw0RgVZrnMzfsVeNfP/" +
+                    "tQVCeOlsgTa8x3j5eQsR7aJi1taen0BQATTprJmN0428+g3Sgh+4eLslkGfi" +
+                    "9c5Ftpq6vM9bxE5w8PxjPdQdGQZdcNkDHEreAmGf8Tb/r0ODx9wMdYNRqbq0" +
+                    "Uo2Y09Q9zm91bPC75IGGrLvzc1X0MXqQmQ0YsRFq7T8j9wZqS6+bveA/Svd8" +
+                    "pwfzfR7GWDCd1lwzuP9QezVrYYcozCquJNMFgCu5hcCJUb0RdC8EBsxArcP4" +
+                    "iEM5+R62j5eOOBMikMgLpRsTQCFxBgFLiqv1sLBe1xxPIjQhtx2FJvuxd6PS" +
+                    "zOVdplRe8WbbN1XZUW5UQJH3fnD8OLpAlWS6xas3Qk0ZCl0jNqUYANUmnuJx" +
+                    "SysQeI4HRF0pvthikuZBzpS6gkxDf+CyNbJa8DZrwc+cZje11KWkthn+DMdG" +
+                    "sODZI4Z/wUGBRB9S4QQSaHUajYbJ/wfiYopt884ophtyjW14oBs67hmX/nZy" +
+                    "cYxmJnEaqcoJvSDzVDnK7YVwV6dV5pmvFe20fWuk9nAdtWbUyk1dXwZrtx7y" +
+                    "Yw6N93sAtlA+a0xwKDWK30PUU0DXuVnaw5pejrHznj1gM+zCycZ0jAQkq5Nv" +
+                    "I3VkSo6+mDhD7VfR16p5+cPgjabFb1yJYjit44H+852Tr1bzekpO8qdDw0un" +
+                    "AOjou03PaJURRqI7E4oHPA9kRIPEHzqJyIxVJrly8hSMbbVaviZOLSzbxrvI" +
+                    "J8qWlIenjvtD2m86tDZ/0V7QEek5bjlpr9wY8sEGtBhuWdRukWhHLm2RlmPY" +
+                    "2pqodKPSiDxAy1mDQiOEMAhHOfrABDKroLsVOBa+zZyR+MzUcBZk4mhXZUmo" +
+                    "dTaMjxYykxeOqnnlapKaz7qwFswwRrXP7n/Pa9XTxKpJms2s2pJZdKhZlAvp" +
+                    "E7KNlmCV8ag2hhDF300Wu+J7syVrNffpqnn4jT8bdgL8orfAXT3dgTzwplni" +
+                    "Fa2C5FQlaeGSG/7yM7qUXChppyAOA0aa3Ujbet9tf2PiS4+dsLjV1ynanz72" +
+                    "zC0bbfzdhZBMY/mYEq6GBAH8lHpAWaoVda66iIoBjNk6+qtv/ShnEFmRawLy" +
+                    "3KWyNeVw4gJjvejp+4Ch2I0zNssPrqcF6ne3k/FxB67eng7kqkFZRfC5xbLq" +
+                    "NVG28rtlDCVVWkcbyPgo3cMaym5ZEj6Hf8E+Z84PuFhQBw7gJao781M0ddV2" +
+                    "RIypj36LsxzzhpSPfmM+GHBtLzvryNqxlzViaHcsPBoh75XL9tQSPVzT6436" +
+                    "5c6dzaW7lkak2TiPoWucxZvjh1PVqoijWvHv/YGav9ffjJef9TfREsBQG+ym" +
+                    "Sw7Z4l7RZaWjfr76sO/K5FKlUmw91k8LyFeRtZ8gBIrGdV1j1DsHktHuH8/K" +
+                    "v/lHolVp0QPI7vyrXRcZd7ccJv05dm5mj+CNw7JHvAHeIps1goDamOCROexB" +
+                    "ksW7YqAuzex8xajTKtaESP3D1kZYz9BZ16sM5LWNjPNdcLygVHMBV7oAVepj" +
+                    "tOd6BYY4xfFDSWT6UFcz0v1GgdsQaszcQdCoRL1XrCatRKjSvQDN6QGXUc22" +
+                    "Cy/JqD3HHc7cqL8y2WELIPZcLNyDFe3P5Psvkcs/hysQPs3XGhfbWrvSarmF" +
+                    "AlwFT5hJEy+pXeb7x+jNjOxaj0vq/k1gyXm1y/pWPZvB//MjLA8tU6mQCyUC" +
+                    "U9wjAtrieJRAdZc4pqqO7Ha1Iq50vtdLu0I2mNn6M9/b2IUfwqziE+rBdXH5" +
+                    "Wj2n3+TQed5xzpJIqG7iJQbOEY4byZgVmSQCJHjWBU1yaK+gkTGunPbv56+P" +
+                    "PNYZ5uUJPWCrWSkmRow4Z8B/Gi1IMKkAsEsv6i6HwomZZoj9tZGpXL27ZKxC" +
+                    "fZMnyIN3QMXrtCb+RHcRSTWlgraLfVMZvjtYh2Kxwb1wB/Lv6ClHT/A69uYZ" +
+                    "QNSDQCCxSOaAOQoACjh2bj38g08nA/rGXUYft9LTEKIkqUSf4fMTc3WzTaqN" +
+                    "MzZ+iXnNMPDYWSWnVOZ21HpkOoOID0zRbk2jBy1B0xW9kcS86ekhkBXhKYxw" +
+                    "x+J1k3WqsMVnsVqPWWTRU/G5OCfgsVXfYElFlrmM3f3jiMADkdBsfEqrpPt3" +
+                    "0QsVHp4T8Q6WzkOV8D5lIGdLKsF++8LSjwLjhERWXdwooq+6KoLPm3cm9Tiu" +
+                    "LMjtfIfVYfaw09zXmKqOkVONEsZDLHTztqmNWNJJB1Ay5iL7QPvSjKQS9WIQ" +
+                    "TJOARZD0k+qUW/9a3QHaeg3O9aqYFMxf7QO4FCHf1TIMNnr5LGoUIxC3n7Ki" +
+                    "cB9MlAunuFud8DiiB8/+3QgslIVknChjQiCeZPFsJIszUvPooQJsRDcGXADH" +
+                    "kXInEcaqT9EsHYnWwDtmZ7gQd+NgT3IqvlRJRZ6KXmyuphamZKieRkOIplHV" +
+                    "muUq1T60+6tBHld2033XYtS0qY81/fOY8WbvjCxUjF5xu/So1tmW0tqr0l3y" +
+                    "GGp8jyNE6vL8/gJgobfXTVgnZhPB86D17FNWwEWHG/dBis7gmo0mkZRh9+gk" +
+                    "PLAl8E3UFqWmTLknSqqcI6ajgkyI3nmphyLc5H2l2mUYRI+bCiOunlWGGzMq" +
+                    "Qj2oSuGdOYX/hNn7MfAYmb1WNwpopBJYHA3VKIKCH4YDLz8h5LF9v1iHpZT3" +
+                    "IiHf1WEwC7JpI3Q1S09sJMjSih5dctNkmoCG56gJ0ZjmJvYhWy+A9/Nyd9qp" +
+                    "oj0E6j0hf6p8dovu1eE2BqKRydCa0T2i+bPnNnB21KU2MLgV5NGw4tyIfUoR" +
+                    "Ui8QSHDynL1Ob1BUbKuqT5p0Ybqths/oWeBN032wA8DOblale/gyz0fXWWTh" +
+                    "VqD5ktw5SGRJTmA/tJfhK7kFfMdk+9Fsvw/yV735NAcTorVbOPJoEWSsq7k5" +
+                    "vY1qreQO6tRd6Gx8aeHW0st8w0Cyf3UpLi8x+NcX53WJaYBhXXJtYZlEnZ3W" +
+                    "do1Ekrs6TWrnRzSUk1uD3Ku47Gpd02hOTfN0T5lUUjaoqRLuvwyYmQiU47Ww" +
+                    "Wnb5ftTAoljtH2P4LXQZyqYWca+BgHdSjlaadtJ7hG88zgSHomwu/QVSVRSJ" +
+                    "zqW+0ekjRr45/9gKNmaDdFwZ41HBgly98x4Z1LzcimtylmgTpAmowJRCBSV5" +
+                    "uad7WgR5Pw2LKWx8YnPAD9aJ78DfSA4LqkULHOfVfqM1CIDO92Makz1gL6bM" +
+                    "6jEpuNkQHE1FifU31Dof4JeoGC+w6V6UjtmUrilKgq98UPAHHU8Bs3ADfXIH" +
+                    "w5Gz/LEjwmUL5pEeFft2PMzf9HkgKcuCFrg8by4PgIv4wMiE6gXZCMfE+Mzd" +
+                    "fulMuy1opZB6LdObgrv8uzhfsplRaVl4utuTGqg3ZE7PyZ+a2nVTBDj3Blx9" +
+                    "88gBN6wjC7MnTRR7C3PlVfX0ApBjmX84Eu9AF7R0zc+XlqsguJK7KKWq9BFL" +
+                    "xXETLlygW2oux+30km40WiZC70wZJuG/Y8NzPwZd3JiJ5/cVWySyppfBD5cY" +
+                    "k6as7/9oXDj5OfXilXXJNnzMSp9Q9h0P0do3qteAp3um6ixvnE/unKtCDua1" +
+                    "KuYbV9ThI+RedYYkYdyKSgBiFxfUtMmYw1E3tqjxm4vNlGJijmBM4HbEmR3S" +
+                    "okiE+52LKS4SiNRNfp6Hbnghld56n/Bpjv3jsMlwES7415uKmAfm7pRQrB6I" +
+                    "6MhGGYhpATf+q3iImH5nSkYt6OCyBjjII17Dkrs+Au/wOk9xhY81j2lua4hC" +
+                    "VAWiv8lXTznHxcHl0e9w7lr2rqdl74byKD0ey+GT15C/oRYGM2LNTwNK/K4I" +
+                    "hh629dJF+od59sUGg3fJ4qPM4cK0M81VxE86TvZP6Nmbah9L+uqVrnA4IT6O" +
+                    "BmiJub1LG0awl59Khw/Nvc1wxje0dr6cuHQuXM2CIYKfaIyjs1snuCpO0KgQ" +
+                    "Tkz7DpQSe9RUo6aC2870GuvKlTLWkI0WI/oRLU0sjgssUXraZQ/8XeC9pS2X" +
+                    "d5FlZX+gA1OM2x6v/DQLzDeN18R6i6UeQZSgDMv5PeCM+1amS+wVznR+2pzx" +
+                    "KNlyPta/4XYWhX/2GB5lwHtYlKDOnI8+0gWU4Lp+mrfKSYdpvnAg4SKRwL8V" +
+                    "h344eiwCCpdDDOuqN02dJs4H552sy1SFwMvBnsJCRtQUCq6SPiJ+lrVTPsQZ" +
+                    "snJcwOKQHNGHkWs2rTPU7nrhbXaRUCcTcPKknAg3wdORtT54ASE73dkufPUS" +
+                    "8hgbDJB8EwD+nKy0Y2y1KdsCCHjoHqtXOwaCoPHecBud71ontPj0XCZ54drN" +
+                    "U4coNXGJjUtKhRPMC45US6AJRYu37P4crc043eO8D+E9MgE9cm8zCF1aRJe5" +
+                    "lP2uQLvGXvg2qdbrQP/ebQNZ3QJ2LUT1km3ApZXRi0ht0a9SzWsRo5CSQY2k" +
+                    "g8K6YbslkxCT/AvYEGWjEHx2kBj96lSTYYz7nXkJHzLX5kbgXWAHavzWN04L" +
+                    "Hqf6pbzbbiNpJ7SpG1SUxZE9xU34BP2msOD4aOUPBJCfd3pE+5Nrnwudk3r1" +
+                    "Lwp9YBlbbI2md/A6z0H5qKrWYyViiFbfmUKZ/BrSt8+g8Z0MY26V+7wQCmZu" +
+                    "JbyOQkLbPG3YghMUK/T//1NbEsgVK43Bh5mvMZgLOWFOHf360y1RBNtJgcpF" +
+                    "/Ckig/TSA25XC4l3MQ0bSEdszSjUkRlKk6WARrZ62EpCV5mNP6XMJOPRpNRl" +
+                    "DHvumo89/PKxn9/0GqLSGqkC+yHxOhxqF/3pe9wduSuEamM2FFySftqO0BKU" +
+                    "uvYDGs2USoeZCO4xXELDn93caAYRCUIEzFYU6dT9JGEEgapq6UrmiS2Hi6/X" +
+                    "zYcs41tJtI143kOJ6NKq/YzvZTNnyOiTmXuKJwE5R4VdtkZ2PBD8MZvqMpk9" +
+                    "reFXpubJBL9z7ZqvvchZ2AHsnKWCOQ/rG+rRXTuWMDF5hHIhr8NOy6Sp8X1k" +
+                    "DC49CFKkSv23OxhLQLfRBQHcKQ5IH0sJTtbHviBq+vB9OV8dz3qpLvAbTB89" +
+                    "tPrKfy7jsfPaFEJUy9bjJsk8gg70WLR32OucUO5AStWk39b2C6PEGewzp+Pe" +
+                    "uy/SWBly28gN3Em4RsHUptWkyiOrw7sQhwalJV60rwqROCfSb01jLfAcBgDl" +
+                    "1yZ08YSKsMvRGUCRID22eDBxl8fFeNHyfPWZrsGegLiWpG4SEhdz9MuayNCW" +
+                    "+K/rq5wXnvgmVeDx9rgeCSuEc2+iGgyirvVcwINSPoJIejyfLP8A+dNQrS8b" +
+                    "6n6ZWKEpiK+4K6gQJArgbdU/2QdzHmDH+b4ITnrhqA2SVH5kjdglYMWBZ45/" +
+                    "/Q5LwMEdqv/P48eWWxN8XMoIkVf+FtqeHwfr48AZhINxbQROlqBS7hCNYj44" +
+                    "BLipDiGmeGI3pYeWsoZqu7hCRMPb42ziwSGFdvv4Q2HFWdgDYvirxqh2SbgF" +
+                    "aznvDQxq4Zzk+TiJbHgphDZJR+DEHYX5n9HbgLWOp2kYWKWT0BPwzS1w++vK" +
+                    "+pMEmbn3C5d7OEBIJ9TTD/jZXB3KP9tUICFFaFXUrk2752fBfO9kA5xxbxDk" +
+                    "ISUvhwBgiNGxet1T5SoRKPb61Zz3NY0SLr+GD5KfH4mJSF6tOUZ2iAP81uUs" +
+                    "fwPIxqHh83mMS5PW/DAEeJT2FFcBMVaXdj6Xc1hvIGGXYjc3NQLxII5vbQVR" +
+                    "3UGhRFSJEi0urSXTFUDMan2AO24mM+jnNRCOxOIAx98qj6DKTfkcBZidH9tG" +
+                    "nJAOlISyY49hJvSk57tjc0oiSq8ojE9bxQQ007mc+XnKP9/5dXrTa/zUhCZi" +
+                    "DIeqvdOZ1ugRt1garJv+BS0kRrMpahYxkqUJijkSE6U+4+5V3ssWhWu+3VM/" +
+                    "LpKK/sKXImOzcBbcdvzPd1/2yUp+ZCzDRB9qeOmdhZtEgKHp3b2Xw7211jfP" +
+                    "ZWG3ydrVCduI2m2Vo4Lb+4NddZj3yN6xeurZw/JZIuzBcmOg6viEu+Be38GP" +
+                    "VWz4DV3lAK/2eVPGUMVIVXthJCFLKaT10psSNqFYWi92OxhOIu0End71Gb1H" +
+                    "tupvmdPZWCQbhaW8l2RvYMZuJFKpH+reSP6FrgqmrxXGf+2EXzz6PdbeLmnB" +
+                    "UB9QcTdu8tsIuUvzh2Axvrxmqi5rigefuQqwvSgl4KPC2mFI3cKrVJ3kB4Nz" +
+                    "gMIpgW1FzVkiVQSHMTno/bm1LxzGv5Bcjx48NbL183kkAb3kYGNfZHEE7EIN" +
+                    "/3EgK1RUUF0YC9EAYt5U/hnNOofK5rNTjYPepexhY5/4Ve5msVrtn3C2Nlp7" +
+                    "gpnGtHvcm2yoPuWo5ASHh+wGZWwkUMz19x7176l2GczPSajuK6CT2i3xnZ8Q" +
+                    "VGQALJ3Tg83Lqg7LfUhQIFYr6yPttZAjuETlWXxIa2IDsxj6Jz9WBzQHr8TE" +
+                    "1NY9XmTY2eenwpmNOVHrwn3ADzW6OyDHfjv6IY+INC9CAnGSf1EMiwUPNOpr" +
+                    "oYhGfRzTgLLJQTPTWwkn4rmds8eLVdp1gF4uK9fEdY17nJFe39I2Tui4yhwD" +
+                    "Ue39/dGYG16nNTqYj/yt07oilo8PKAKczLLN20Le0aYmOBMGWx+5gWGiPQGk" +
+                    "Jmq0LCHO5rHf7L1j53+efwedTj1Z+qv20IQINJcUvgUX4XfJewiUoLU9Nf8P" +
+                    "jOTIzyR7lNWV75kK0JZJz1jww94GjuCq6F/qXwAM3P+X1VThbLKd+drT3YmP" +
+                    "WVOeOYWPOXUrU3OowSzMGIE5sq7rQY5WwZWpE8O5lUJ6fZwx+MjKVnAYdzAJ" +
+                    "rRxH7eBJOFwn/IGlgJDUdvOHy2bXX9kcVP/ShWuflU1e0y158UTGQNiIRjN2" +
+                    "E19RBBgTru0eB0DZ/yke4BhkeIo5u0Rg91asZHp2RHONDyTppR8wSO7aMKRZ" +
+                    "BQYEv87BIO+B06YsohMfWa7GwD4jFd3XAm12aXIukDo8sMJ6f/C70NwyNsNB" +
+                    "wvjMvl+8E4jK1s5qgagPwECIdl6RBtf0m/CDm3FS1jD8ghJnvbHG7aZnafm3" +
+                    "jTPJcAUDP0+GGtK4aFvqC6yvbUPotlWYyPDhMaOOFSwuFUqn9tzlY3NPlo4x" +
+                    "28rietj+sZjSd+fdUiPP5q7T/VR/SsnzIWTgWOUyEGVeJczUpSdqNO3MJoY+" +
+                    "5MV6+KRkRAbULuVC+N6whOfPW4aihfNVLEBkUfkpo9f0by7fpVU6eJTaqv4T" +
+                    "Z3qW1t80ZVUXN6qRnjG2t7UmTqP2rTXurYseWBYZceo59zfQQSMo5cirpLbh" +
+                    "SlCrVXP/CbUM+OMoiudWha/RZw4fdximFlo6R+TxMoWdWOAMc1kfyL5q7tZk" +
+                    "kJfAj7r3Y4XBcNeQ9bqKKdBzJxUNRvU2Be47fEfbVpKqDWFlxeOrj6tRqjKK" +
+                    "YUt7zjZpYGFqG3YzTfXZVSQ6Qwmxu/QM6vFVjbOpsScYal7Io1qLnDBSUmRb" +
+                    "v/gong0EpsUNvjQ40B8jmNUNsTnRF8memZC7k2cKLnah5RXyuLFvN4cZFSar" +
+                    "LPhBgDBWRgtEzeShPtQiYzJCelbelDoFBz/KfavE4MLRYgXf6/AE3U7CAwDq" +
+                    "splXWaEuCGw7Snwpchk4gCXSiFjVDj2ao2WMA6y/zot6a27ppekkvQRFCtkL" +
+                    "SXNkgJ/F0mvgp3mQBOvo7Yd4NPJV++tRFf4tHWbb8VIopwGynZlnlxXyil0c" +
+                    "P1tRMXjBWDHFY5GYv48XTon13fw/GORM7XMTbv30KXQvTGpLk24IEjIG5iD1" +
+                    "MrqQaAgkWkzBFPwnZpKIenpGmO0G06uW0+c3eJRbfkUaVlsDCnB+3+qZNYSw" +
+                    "ugJsWuMUo/brItbV3Yrt2012ocymZSqOGtUewz+VSOopAHG+uIZi2L9h3/kj" +
+                    "cJr4b5QSsmFoRCAR5j7TSkVpsfTK6YH43FbXxOHamseOOE18TY5BLb/tIlU+" +
+                    "i8p59WnYo5sDy20o/sDM9N8L0Ks4Vma6DIdwYm0YGRzLBR9N5i+HjcprWz4x" +
+                    "FxBHMVMalm7metqsOihfb7aj/iu36uZ9eAEJNZra2vUz3F2oFwlK0aJwznMT" +
+                    "+k06Rqd2xZOKZxWqbVeWMb8EPiAvjwtAbmoXzTf7AZPChwAtb/fkd8QhKAHt" +
+                    "Ge1akmgoY/puFLMJnFECvmy8dlzVfsAX7JhBlVRkeEDofUDCnkh7brT9qqrR" +
+                    "gj92kv+jO5PS2svsFJa+3bB6tuKLe+AHnuvaB1aAdf4aPxFGwHAS2a5ANqJ4" +
+                    "CskScf9J9kCm6KwO+CmhdcjlZFMs8S8CV7XVpNrlwWDV0CdwEmZMvIuIvUB8" +
+                    "Ic58/kCZ8A7OwUVzMEtmJPuLBDVNrS2jLTb3CKjWw5EO9H15J5FseeTz4jWj" +
+                    "7PfjV95NT/n0IqhN/1CsozW6Ko2LDrzH9AjD267p/cF62Hc0a7XI+FMF+d0v" +
+                    "myz/L8RjdZzwiq/Xg+gYWLT8uAv48yazhy+3h95Ttf8IP8E1pSAEvyiuKUcA" +
+                    "liw8aOjVpYaBJU3SZEy/T4QAyY1wmiyaReHXZ+ayWHB+HIiCNdNXj5koUOUc" +
+                    "LVKQBdvgjvMMLIUlUCJB+armPQ63GNp5bBfI1lhvjrs/He1HfgKYf8H6kWwf" +
+                    "BrBSYVno6HkRNKutUpc2nrXCHRqmJtuzB7uabtGFQEZbxP6OHY01k+tLPHUz" +
+                    "EJ/tUGZ7mUfZp1m+PwbXlSjRMYJhLJPsNbczNkuFdfIAfAN8YlUjaeHQWAuJ" +
+                    "KaA5iwxIqENVCZR48WUI4s/1uXO3VNa+UoSGI5N9FLgfsf/m/3Er8djDf/VH" +
+                    "kW6nG3u8maC6xuTmHh2w0QSsLhjqkjuIBoY7T2Ye+phZYhcjLTuRkoJxNGXv" +
+                    "foVPuNghwAHm2Jw1QN0LjWQp0aggReKLBIAb5sTLYLQltGHDNj6ime+gQmk2" +
+                    "2kb2QlCP2sOP2zs2/D7CuUWHVeeA5hcJc2eWEWv/I5s6cYysTM13dleBwIu0" +
+                    "+0Lyz34cvXrC+9agwHa+uAdO9KYfuk8PjRmVPY5rgL5ux7QbhgA5Ou/1GFm7" +
+                    "66fbdqYHNN48Dof8WeNQTZE1qlHl2ftAKyFk9vH+2T/1knJVHpEo2Dh5KywQ" +
+                    "M41C0Gla+cIj+Wpit9hVYyF+VMC4MYbJDDQ/f5VCuSMXJIFZ7wwV5EkCLZV4" +
+                    "qlDC37hMec645uyutF+SAFAyyiOufGnyR/spllpPXf8pF/HXvtwkPCyN/IKE" +
+                    "vp9YOKtXdGfQ0NksPRP6f/nfy8X49Nmr7N4oexlVB/2hfym9ApWw3zVMhWpr" +
+                    "sk9EWMkVzNccL084P0UBYDRwVmaU0OJHU1zDs5MS/joegbwjO1gA2FJcFdoE" +
+                    "vbbLWCDJGVkZWvfgPr28xxSZAhYuFO5zJ+Sn+36NXPsAwQlbyMWp7y5R/JBH" +
+                    "zHrQmpEp6uyyyYqOFA4QIWW4RhBW30gABmq+Dc9qqEUyVjRd+8RXf1Qvin7F" +
+                    "tD0cTAOl2UISjI7Z7JexkSFBcS1uEEgG2SkGYycFa3TapP2PXEdL77pN+FJ3" +
+                    "tuefmxbW4UaQq5xDr59BmhJ6bZ887I6vwi1t5Q2rqTNRxZjePkCqhtZSV0E7" +
+                    "JfWxMEPxxzJz5e4/PMMdMUeN2lG45WAbdJuWYsuC7gb3gVmtCkSyTJcF0Xos" +
+                    "Uh/jsWbVPsDWOle55iWepn7i0HBUQONI0iv/BLFTbU1+19frmpB6MEmV+t+P" +
+                    "cUeiVWy2uoRXXJiZgZ6uWAPtSedlFNO3DGCl6a/POkU0OBvW5UpVlpVAJ2qt" +
+                    "qXbBz3OYJ5eOS9xqRpdvqtkzIB7eXO+2Icxq281+M6Ug5PFRgyLpk20RXkyR" +
+                    "PEqmjBnTkR8Ku2B2F4SvQJsKK3dxjGdxwYr9+3ReS69grg4/4WtbD4iVf/lE" +
+                    "G/UrIyW4nCL3/k0y2LL/pdbCPsoT3kSLvtNxKF7sFnLxvxvPxGMvYpnGyE9s" +
+                    "UeOtPY5LsIjRX3eMuT+msI8fPrlOcZ2ejz5FRYvvChBxB6o9w9ybZlxx0SUh" +
+                    "1yqqW+hjCY0bfzaOxEVs8ZfGKgfS16AVN0sArOBdS+FhK3zKSAubqx4IfZ0y" +
+                    "luFVMPAGUUo558vyGZNhmW6U1FJczZWz+yFOZMcH9Y21GRkE+JKnBIEY+cuM" +
+                    "RolrU0Tzj3USUC8xazB6ipNZNKepCCzcRy+B8ulGVeaZHFzVLe+yt1zPvFOJ" +
+                    "BQOoGDe9ZDsqo1QJHxTh7eaEWr8hMfLs5bDanrN8mBjhom+1Ni7RQEg7qtbf" +
+                    "kWd6RdB5gDEZDeFNP/16n1dFf1M/lLYfKWUuYLXueOX4s3dFFAxyKqy9NXVk" +
+                    "X7/s+d9XTQfIax6Zh3SH5YF47yddlysYZu/HjhaO31yq1+hBLQgeBEl91vnT" +
+                    "Qg6DlMnh36Vpsfc36UqKo/97LMioJq+4vViL2CqMlTboeepvgOMjdm0VfPn1" +
+                    "cN0ithvdPi0pOPjArFjhcsdF2I9qQpQCPfC9n4xpaz1E1PQpek5x/LqueH8F" +
+                    "UgiOGoUZWSdRaFjXu9KPP77mayqTT8SnCx0flb0E92mEpI4P4zq58/nOnVWN" +
+                    "uGcOZ7AqZRwIfIHJSMlXgaHQGg8YDoDXJKgp28fZZzESjzEzKlE4zwQyyAFe" +
+                    "jQSXjRexLS9YqWpWaIFaVaNy6b3zIohOjLMWATB+OeuR48JfUKIsaBUz+HdA" +
+                    "Y33pgtJsb4tmC99SeS1sBfZAw3hUYWUE3y24+u6lRdw6AX3SvfhC5Vc8qVt7" +
+                    "+GFWGlOH9MdHYcoNxPpB3N0HqQYZk5uGgcTgHneJ3oq9QLRzURvzjEupTzI/" +
+                    "z36YZRA9vm+TgWoCrhUH2/52vJhKgjOY4usDvJFfEUWHv61fOAxzy8wLgu5v" +
+                    "NXBX8ABR1txwpN08BCs1I3aoODlTz/T/WwPNfGptkP1sQ4PgHxJdCQsvi5WM" +
+                    "L2sBq/ZmIf2SVNQaRI7MkeS0z3NdnqxzY+8wTVjQVZG0zbpNpg1G9fEk6Rkk" +
+                    "wV0vHEGXnZJ3DXTzMexg6NSyE3GXMJHa9J2fP4lMuKc89XtGaA+CU28RgbkE" +
+                    "Zs+Mlbu0YNvk7JABJ/UHxf8UNJY9YG5BdUvJbElrbajWnllEaRauDzjnvjkZ" +
+                    "U4Uw4fdHmzpu0PUuM9Wm5UusD5lZfj0Xng+XO4Kea9RheukO7CCf01jZuFlu" +
+                    "Xgbi1tbOkkVA0AvWbnp2Dv59O75kaaVq4kBJB1SxUFb2DTvHTA2b8NYEfeis" +
+                    "eVIiKNW9fDMJbbmwySQftx0SRcrKbCloN8q7RAzmewiyVBtCI8a5CNw3qtEO" +
+                    "RjZYDPmLXaFU+uc83/dIMh3jnJcbZyUvt/X1NMsrF+sSonKepfVhq4mtO5XM" +
+                    "lQGTY5Wyl6ytdlMbK/vwp4MsZXFsAzP85AOpEI9cHUnlYM/Q8YTMeLZt60hD" +
+                    "Mqq9SU0USmvqbhteriLTuTczrsaebhXHsPbdcq1zopsOtA03q2hyQPebYWP5" +
+                    "x79K6lUVxOar3JvnguVfyOrDBPRXDphI1Ew5g8dlGP5OoOhGWT4/S4OEIa0K" +
+                    "32dRruB7A/F31osJeK8J2pvoosZzLpxh7nyU3Jyc0c8uuQqj+1NiGvERfV9W" +
+                    "a2UkCOHaLwmNRb43ifkYIW9R66mL3Mu+hWo3aXfDriYxZHG61blhFYNDxkUf" +
+                    "qx3BlDmcjbWae8v6G/JI7mEK0XKRI5Z4NgheSSY7VBnj4nQRRht5efUq1TWt" +
+                    "OpK4Ws29BRhSfgd7DtpVS9zSW48JCfxGTuNHHbXzSWrLSAU0OX42ycv74gm0" +
+                    "Nv7XeLzs5Bp79Vkc/YZRxGa65h5AfQzKf001czPofnw58fHa6WUqD5m2pEBC" +
+                    "0fC/YSIpV7wJfUBuj/JlWGiwAkWpkQihq7mW8GcxUTk2nLLrBbWr4X8QBdXZ" +
+                    "s9dA11UTul6nVzGnFDrWRbHgcAmU4bxSEsMYL31tGg/jMKPx6blN/M3I+KKN" +
+                    "U+9y1Arn/36z471rKz6Xgo5bP4qbRxLArWnmV4BpB14hL0JeQJaqJtFuNIxw" +
+                    "+y04cXQjdHxmjluTC5F4hViPS5rY5+AM4C7IqR8fjmC/pLNhWQsnz3/rH2Ax" +
+                    "3zPs0E3vdeZl3JRMUeWqRYLQuF/Q38u08/8PthUhYyT978xWJ6+t9rrC2iS0" +
+                    "QbRNvmqE1TuROy3JcLz8ig4Ryo6thY7tKnKuRFmbBQHWk+43yU5hUqw2H3hX" +
+                    "roIYd9NqTeImqfesegSnRpSBnOnFQLRkI8KZOIQeyYzKOWVJQw16xwSX+PdU" +
+                    "4hc6pA6+pIm5k5KO/UzJGmWpzpH1HyHuSIjhLi/zRGBHOXPZHLtTHjQO0fZs" +
+                    "WH1QcmVrrrRvHhTXzKqKsQRfKtkgjQRCJtNIB5tQjWfwNgW4Lu0psB3x0sSL" +
+                    "GnKm+BKjgE7HMApctusBLXNwplxsp8PYyZOiQeL+6v+iUCDLDLI5Tv6XIONW" +
+                    "GzxU6SAAcVhJ14gy+95ptYSdc9YioUHvAgcletAy7oWdnpIliUfmlOELyvZF" +
+                    "kpixQDAyNSVUUWxGsad4dwYRv+Q57WMWkDQB/WfkBfqK6EO0DQg0FvJSQ2Ij" +
+                    "Fj7O4vOPDyXt/ir1QUxfELactILEpBTf0UUgIJAjcnAI0waPu9fN/619eo7V" +
+                    "VH8Wpk3HUU9LjOIIDC7BAPPTE2xDa3kAG9VGweIXpL6YyW4JnAVSkou1pZWt" +
+                    "CW05eMRe0HkDpEJb4iz/UHn2KII4ZjJaFUgSWgNDRiHRvMoW4tIImHuI4+Y7" +
+                    "rokPlAlmO8yEnsLyBVgiPr51Sq9RWPCL1y3bryOfUoGFc/cozQ1csN02Mncz" +
+                    "YeSbbwt0toufO1ex/gfsiteqRgZvSIn91yMsFl7lzEi3kyJIhW7hBrPDfAHm" +
+                    "TZ5Czrcs1z0+JKfU1PXcuaRm/Bw+OY/1v8Z+YYKIynupdgnLCY8LfruNzk5S" +
+                    "e/mT0UwgtoJE+TvceiYGTw+Rkf0a4jZiTEcs3lF/IRvIOnd/+5m1eeWtGJp+" +
+                    "z6rlPrlRWjrhl4Ufqxob6U5jjY1/7zAMV6Ge7RgkNJiRy0Brb5+phAHpkajC" +
+                    "I/q4m/qBBm+gqRrfmFvPfo1CeijfMelIXMh3+p628kWGpIAypU3ocEcVcXXG" +
+                    "B+K/n2zyfbBQtMQ+MEUurRbn4G90gyRaHKgf3SuQSM53c+OVfJGXhR4Gtki9" +
+                    "NF3kCMt3wiGXi+c25NEs07LZ+xWfLeK0/0IrAHCmG8IcoO6T8pujgZUEyIHv" +
+                    "9Za/9vrvP72UlQ4+z6yUg0RNwlHgWN2nF0xnTQZXySXaoAaqwGA8HetCbx7s" +
+                    "ePd+VFl1/gApyxeGhWFTf0d3YiBLRCqvdFCsKrC/ni7aIGgbpuvjxmHiTAKa" +
+                    "Jaick9xE7a13b2pY557OVq6ExqRiC3c28z4YuKXfJf6IIeBug1HY9yEnjU37" +
+                    "eqNJL/XbyImo55ARAH61MwL1RSqu7uWJDui4GKCOKC+piqOwLDjSx8AyRVN8" +
+                    "zbsTxKtN0N0AUBqstlxgrfbV3/BCqKSQHCaqlg0Nrzk0T53RZEHyNv16qtr2" +
+                    "iF45GMDj3k/lP/NLU4DQX3R0zx1dQirSfUr0PJGxZdwnfHFzSZ7o5YaAnqdW" +
+                    "j8t4DGsu5wdktDOfqRsXrX3eKmeps5ycRBKTUgPhoccH+PFtMXYjkTH49aW4" +
+                    "hKgnJd5I49A5o/Vdd5Z3zNFF7eSUsMFA6pYxXOy4/wQYpu8vqmYBs5p/WBM3" +
+                    "wJkp0iLaEsEvJGSRNjbyuhV+N16GamJVIQVk0X2HvzKVtL1cHHN2f9BkFbFC" +
+                    "R2iW/I0ZFz/OECDOnlS1Ea/jGXuEIOB2+4O/Fki7kQ1opkc5pB5ehMMCetXP" +
+                    "cPJrEyyQJXsG0ZiCmr9RtGoEKG6lfvNxFWBtXHjXrFXjZ/KRZi7BS6ZPIRb+" +
+                    "YER6V8tyLNkL86xTjkm1oq9+opihDSf2+dve/u3YFooowsleeNynMVEyEPo2" +
+                    "yOLB8FO/l60qpSOSrepG6kS37ifjrv1piMyqLxz9EWP33jG0ajCEj1hAoke2" +
+                    "5uLFL0xaYT5Hbb3SPD6E5Kwm3GrP53VMGpzxFb3WirP2iW1pSLH5ZwBSiiJa" +
+                    "awGW/1JAZ/AemOroG1jpFXYG30euz9cj1ha97GagqGgaklIQGbGejqj1lu7/" +
+                    "H8Uu47sdFxrQTbs0enJEr7iZdOWHb/Gk1u6X2+1d1+2ifn5k4dd2QrKi4lKZ" +
+                    "TnzU6InfbE6wl7kblSGnvxG5CdMs6vfcs1VQHbahPxyZ1ci7h3Yda6MV1xNv" +
+                    "8gru+cCbr/x6v0L0L0rjSg2ZwFq+UCexc8t7ONv6y6KhjbUgGmy15GthHqYQ" +
+                    "/wFMIeobeq7yd04vUMhsvSxfXL54RECwu0Wz9OXfPU8yGdLiUqGDlVM/lrCF" +
+                    "H7zdSU+XKqOoxA/Wb8zayXZehYRQqnDVlJzA39Sp4KtPQdIapZ42ViJ8SWXI" +
+                    "sgtztW588KRMxjtMxpIj5eKucFr9bqfYsaYU74t3JQUIeWrKMEGlftBrT5Tn" +
+                    "BIPhOlTSMWe3pWOkJP76uUm/mqrz/LirLgzKrMpsw32eUSQsizWwXnC2a9pf" +
+                    "VQcqDIQBgIRxI6bp4U4IQIhCezOhZe+02pt/R6el4fvDyhGh2sjcUyt50XHa" +
+                    "tobMMkNVoQTWm1UUJpRB5n0CbtXDRkB54kL7IYlhsZVBYQqDMihNbTvcGfUE" +
+                    "tz+Yw405OdLawX5VpV4azSeXxncq4LlHgE23JKIpZjnQ/ueb8c/6EUyfn1u7" +
+                    "4+lGsqIK87fwPrGP6tgozyo8e+uzYl2wIKjL3p1ypi9IJodtMtQJT4Aw/Nyk" +
+                    "d+fB2zS7h54vyh9XJBqXBlyBJTDoevGxACfCZMOagdR4kmysiWUEqcaPSslx" +
+                    "9is9VTALHRpN7LeKyTnPnMzFb1otJ+tYncqtvaP0I8hRgtWHsbiBqpjDUZxF" +
+                    "nOXaXzbuYdEV2rzcQ9msOCqejRctklNVGwrFdqEKUeOV4QwwGJv6wb6rRYaB" +
+                    "qXB0/8CEkoNYOC/VU668RDOkwTFd12w/9TOgr88DHX0dN0OHLKa7/xzNji1k" +
+                    "OVkQcT+Bcrqr2pUWkic30U+YU7mJSC49tssKTROJ6SM6m25jou/0YuuYTge0" +
+                    "S/bn+Th6Y7YJ79eqvmG2OneqcCwE+SzpehGo/LeROeBH5w3rGTcl2qcxvRf1" +
+                    "SyyJ5aGlh6fseWgcO1NW2oLr+ih6JCpDf4vRcYjdaZysmzB9y5kLOY3rPQbI" +
+                    "OxqaO1KshMb3fGJIcK53lZBqHQfqQ7CgeYotq2OGUmKzr2BVmkah+YIMUrRE" +
+                    "xVGSlMzJyDABEE5cTBvcmq8MJvTdFeQrOHkAHJ4Y10v0rb01hGkvJbSUq5a9" +
+                    "B7f7ulZPnYpWmJZ8iDPs2eETUH/fm/a+B1i2kb7QCs50zGfp6grwmwMb7fd2" +
+                    "DEOkjXS6VEOfnzS4jlNhBfGRD0GORIjRCI2arB1hjQvthLU14Q03ZetymTXE" +
+                    "dMml0i+t7wNhESkAMwvfqkPrG72sg32w8yCKh9zE7YATcElTAobdxR9aU8ei" +
+                    "ulXgMb/PnC15/SZTvGzK4E5FGjRbRYx0ZJsQYpdzx4D0L/Yx0BEODfIdbXDV" +
+                    "kSRMtltmWx+88xlqzN28I43ezoe/8rU7WzQvJLs/9N7Ud9JS6H2pHJxqGYQs" +
+                    "3Sk32tnWjIk57PXk/++6IwsGonmalUSP53cILKDerkkj8LkOtbCSdmQu5uTb" +
+                    "dXdxM6Yo6nHStyJyHZUz52qDbZruVnBLS2HXjG0LOBNSU80jbkNzJ9+uSr81" +
+                    "bsHaUlIsq7Wksrv1pfNIz8ieisEJnk3FE6nitwh6w5RYF7Kl21a1vEPQUipZ" +
+                    "hfPBLGel0lBmBb36va4ge0nketjV01j4lBStIg5c6c+V00gwsu3/Aj+EaMy8" +
+                    "jDW1nle5f1wG6KQxAqLo78BIOojlyQjR1rAbPS+rvxTjirQ60LEPeyEnljmG" +
+                    "xVnGGliSrnlRuI5KAkQxLCUEQTTFy3Dj+1NAUkx4Xoi7ARsfvj0L7T36wq0Y" +
+                    "+aFViAoYfBXqNqf5vLbXo8hcKIkfvUf5b90ivNPnQx/nUH6LsSaDAUEpR3CR" +
+                    "8c3iNn7BURwoIjtAz3NfEgsQRYbyQIb4hgCODp2tnhl+GK1bKvkgnbRoxyCJ" +
+                    "XsK6Ka9lLK6TGP2AG7zdJIBBvWF4yCsNdF06qYuN32L0t4OOP6cRMowDchoY" +
+                    "vwXr9KWAvAc4Kq3KnwwUDPTISLp33n9LFItdgZNZeMjZ3hi7mSisMB4lK0lX" +
+                    "L4T3G/GNH+sYKgS17BRU1Dw/Bmt9LGOmQIFLsKErqIV+cXsBLlNiuC5lzl5Q" +
+                    "W3DTNJhErd3vaecEAJJlTfSjSQ9PLy7nDLv/RjzVKNgXSerqfuiM05McEm40" +
+                    "g6jQDRalN1alU8Cz3GLRjsfNGdYTsh/jPD4+ZiEqnCJo7VMxs+kb38etoTlz" +
+                    "Zs3hoJKH3euds2uSSGn1yPYb7J4QkG0toLIydkwAvMMdIhwQ1VN6bU8cYGax" +
+                    "FtmaugxN+n+00lb2Rbpdp+yVchdeNx1Yal30XPugP7x1OrVAZw5InTNCU9zQ" +
+                    "Fx8ria6g4+t1GgwBqmPi1noSHHG3bThI74oBTsg3L4esRW27OMrTTzYVNemt" +
+                    "dvJXN8yedTY2rVqwbKJO7bGR2VcDDn8ZgoaPBIvQuLSRBIdbUSIeKuLiuHbK" +
+                    "IbkwtZTyz7LvrE1SZ0oDpMf19pb9fZPi3v7yQpFLx0oqqSYdhrJyOfhNVZXU" +
+                    "dc4vbE0qez8MX9PNmyMtt4yktA+FC+2FM4P9jjdvaIfdm2Q7NhxjffvuwBJW" +
+                    "SIoHBSEmxfxZgGCo29pOy2TZt/VpU8zpGpRBaqx0F9L2YzRJgwgOTYbkhF4w" +
+                    "w1w03jCk3UWEg1Eq6kr6ZOEY3Us8DLTnx1RGKeGEpJ/vqtKzX1+KJS1t4E5G" +
+                    "3eqa7WUqcyNqII/9ShMZqdfmXj1llgfuD+31HjDXXVO8RFY2zG/OneMkJJP2" +
+                    "WfCs+IyN0I1+u/UJVMVW1d9y09nbrjruXSjvI2NwALB8NgtTHYbu3aLi0c02" +
+                    "ijIB19xcNdSjwsmpR87lp9VFqIGEPVcYb+E11OODliUPNRPig6kPcbzGGq4b" +
+                    "2CmbVVjPURU0U5cXJv0jqxGl8C7AsDIaJkXwGvQlp0t2wL/Wl66mAqBCQmJO" +
+                    "Uw3A7/NbqGbhky8r4XMBBk0bOBn2jUIXyfJG696QPR27NrMshZRFG8fKmRfm" +
+                    "4inNQQtcu6uUOKcoppeghWHBkK134LCFJakVrgmo0QeUXvdlR79imF9iyFDc" +
+                    "Z6Fayr5Mh0RiDS0DtI4E4k17OVTjPYenflXCSk5VDCN8kM8d34wQEX2lahR2" +
+                    "oWTBvJAFrvMqBL3zjnzjnNavoVaMlJy/Ezjrr+vNraCbNSY69Icflfb0xUEa" +
+                    "RsdADuCKDVXoAavcUKw9aXBTolmHvquBBnUsyi630i2mONUg7ylqJCvCTCGk" +
+                    "EDq48TofcRnuafZt38iwv5PRmRkhnMSGLR3L5AxjCAnSAUvqKPytH4QQ7+PJ" +
+                    "NHc5qetzZdz+jRkCdCnC5YonOWyzi5I20U3Cdl7pL7Ev5INyk8knQLY2a88f" +
+                    "9cUiV5kwPTAhMAkGBSsOAwIaBQAEFM74e0Rbt2IGCAn48XjvdAcaIl6cBBSY" +
+                    "pFDCs7BGKfo6O4hW9fB0/2HXqwICBAA=";
+
+    // You can use this method to print the java code for the BASE64_CERT
+    // constant. It will open the keystore file provided as argument and
+    // it will Base64 encode its content - then print the code that you can
+    // cut and paste back in this test.
+    private static final String encodeKeyStoreToBase64(String keystorePath) throws IOException {
+        // e.g.: keystorePath="temp0.jks"
+        try (FileInputStream fis = new FileInputStream(keystorePath)) {
+            byte[] bytes = fis.readAllBytes();
+            String encoded = Base64.getEncoder().encodeToString(bytes);
+            format("BASE64_CERT", encoded);
+            return encoded;
+        }
+    }
+
+    private static void format(String name, String value) {
+        System.out.println("private static final String " + name + " =");
+        int start = 0, end = 60;
+        while (start < value.length() - 1 && end < value.length()) {
+            System.out.print("        \"");
+            System.out.print(value.substring(start, end)
+                    .replace("\"", "\\\""));
+            System.out.println("\" +");
+            start = end;
+            end += 60;
+        }
+        if (end > value.length()) end = value.length();
+        System.out.print("        \"");
+        System.out.print(value.substring(start, end));
+        System.out.println("\";");
+    }
+
+    private static SSLContext createSSLContext(InputStream i) {
+        try {
+            char[] passphrase = "passphrase".toCharArray();
+            KeyStore ks = KeyStore.getInstance("PKCS12");
+            ks.load(i, passphrase);
+
+            KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
+            kmf.init(ks, passphrase);
+
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
+            tmf.init(ks);
+
+            SSLContext ssl = SSLContext.getInstance("TLS");
+            ssl.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+            return ssl;
+        } catch (KeyManagementException | KeyStoreException |
+                UnrecoverableKeyException | CertificateException |
+                NoSuchAlgorithmException e) {
+            throw new RuntimeException(e.getMessage());
+        } catch (IOException io) {
+            throw new UncheckedIOException(io);
+        }
+    }
+
+    static final byte[] DATA;
+
+    static {
+        DATA = new byte[1024];
+        int len = 'z' - 'a';
+        for (int i = 0; i < DATA.length; i++) {
+            DATA[i] = (byte) ('a' + (i % len));
+        }
+    }
+
+    final SSLContext context;
+    final AtomicLong requestCounter = new AtomicLong();
+    final AtomicLong responseCounter = new AtomicLong();
+    HttpTestServer http1Server;
+    HttpTestServer http2Server;
+    HttpTestServer https1Server;
+    HttpTestServer https2Server;
+    DigestEchoServer.TunnelingProxy proxy;
+
+    URI http1URI;
+    URI https1URI;
+    URI http2URI;
+    URI https2URI;
+    InetSocketAddress proxyAddress;
+    ProxySelector proxySelector;
+    HttpClient client;
+    List<CompletableFuture<?>> futures = new CopyOnWriteArrayList<>();
+    Set<URI> pending = new CopyOnWriteArraySet<>();
+
+    final ExecutorService executor = new ThreadPoolExecutor(12, 60, 10,
+            TimeUnit.SECONDS, new LinkedBlockingQueue<>());
+    final ExecutorService clientexec = new ThreadPoolExecutor(6, 12, 1,
+            TimeUnit.SECONDS, new LinkedBlockingQueue<>());
+
+    LargeHandshakeTest(String cert) {
+        byte[] decoded = Base64.getDecoder().decode(BASE64_CERT);
+        context = createSSLContext(new ByteArrayInputStream(decoded));
+        SSLContext.setDefault(context);
+    }
+
+    public HttpClient newHttpClient(ProxySelector ps) {
+        HttpClient.Builder builder = HttpClient
+                .newBuilder()
+                .sslContext(context)
+                .executor(clientexec)
+                .proxy(ps);
+        return builder.build();
+    }
+
+    public void setUp() throws Exception {
+        try {
+            InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
+
+            // HTTP/1.1
+            HttpServer server1 = HttpServer.create(sa, 0);
+            server1.setExecutor(executor);
+            http1Server = HttpTestServer.of(server1);
+            http1Server.addHandler(new HttpTestLargeHandler(), "/LargeHandshakeTest/http1/");
+            http1Server.start();
+            http1URI = new URI("http://" + http1Server.serverAuthority() + "/LargeHandshakeTest/http1/");
+
+
+            // HTTPS/1.1
+            HttpsServer sserver1 = HttpsServer.create(sa, 100);
+            sserver1.setExecutor(executor);
+            sserver1.setHttpsConfigurator(new HttpsConfigurator(context));
+            https1Server = HttpTestServer.of(sserver1);
+            https1Server.addHandler(new HttpTestLargeHandler(), "/LargeHandshakeTest/https1/");
+            https1Server.start();
+            https1URI = new URI("https://" + https1Server.serverAuthority() + "/LargeHandshakeTest/https1/");
+
+            // HTTP/2.0
+            http2Server = HttpTestServer.of(
+                    new Http2TestServer("localhost", false, 0));
+            http2Server.addHandler(new HttpTestLargeHandler(), "/LargeHandshakeTest/http2/");
+            http2Server.start();
+            http2URI = new URI("http://" + http2Server.serverAuthority() + "/LargeHandshakeTest/http2/");
+
+            // HTTPS/2.0
+            https2Server = HttpTestServer.of(
+                    new Http2TestServer("localhost", true, 0));
+            https2Server.addHandler(new HttpTestLargeHandler(), "/LargeHandshakeTest/https2/");
+            https2Server.start();
+            https2URI = new URI("https://" + https2Server.serverAuthority() + "/LargeHandshakeTest/https2/");
+
+            proxy = DigestEchoServer.createHttpsProxyTunnel(
+                    DigestEchoServer.HttpAuthSchemeType.NONE);
+            proxyAddress = proxy.getProxyAddress();
+            proxySelector = new HttpProxySelector(proxyAddress);
+            client = newHttpClient(proxySelector);
+            System.out.println("Setup: done");
+        } catch (Exception x) {
+            tearDown();
+            throw x;
+        } catch (Error e) {
+            tearDown();
+            throw e;
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        System.out.print("The certificate used in this test was generated " +
+                "with the following command:\n\t");
+        System.out.println(COMMAND);
+        String cert;
+        if (args.length == 1) {
+            String storeFile = args[0];
+            System.out.println("Parsing jks file: " + storeFile);
+            format("COMMAND", COMMAND);
+            cert = encodeKeyStoreToBase64(storeFile);
+        } else {
+            cert = BASE64_CERT;
+        }
+        LargeHandshakeTest test = new LargeHandshakeTest(cert);
+
+        test.setUp();
+        long start = System.nanoTime();
+        try {
+            test.run(args);
+        } finally {
+            try {
+                long elapsed = System.nanoTime() - start;
+                System.out.println("*** Elapsed: " + Duration.ofNanos(elapsed));
+            } finally {
+                test.tearDown();
+            }
+        }
+    }
+
+    public void run(String... args) throws Exception {
+        List<URI> serverURIs = List.of(http1URI, http2URI, https1URI, https2URI);
+        for (int i = 0; i < 5; i++) {
+            for (URI base : serverURIs) {
+                if (base.getScheme().equalsIgnoreCase("https")) {
+                    URI proxy = i % 1 == 0 ? base.resolve(URI.create("proxy/foo?n=" + requestCounter.incrementAndGet()))
+                            : base.resolve(URI.create("direct/foo?n=" + requestCounter.incrementAndGet()));
+                    test(proxy);
+                }
+            }
+            for (URI base : serverURIs) {
+                URI direct = base.resolve(URI.create("direct/foo?n=" + requestCounter.incrementAndGet()));
+                test(direct);
+            }
+        }
+        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
+    }
+
+    public void test(URI uri) throws Exception {
+        System.out.println("Testing with " + uri);
+        pending.add(uri);
+        HttpRequest request = HttpRequest.newBuilder(uri).build();
+        CompletableFuture<HttpResponse<String>> resp =
+                client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
+                        .whenComplete((r, t) -> this.requestCompleted(request, r, t));
+        futures.add(resp);
+    }
+
+    private void requestCompleted(HttpRequest request, HttpResponse<?> r, Throwable t) {
+        responseCounter.incrementAndGet();
+        pending.remove(request.uri());
+        System.out.println(request + " -> " + (t == null ? r : t)
+                + " [still pending: " + (requestCounter.get() - responseCounter.get()) + "]");
+        if (pending.size() < 10 && requestCounter.get() > 10) {
+            pending.forEach(u -> System.out.println("\tpending: " + u));
+        }
+    }
+
+    public void tearDown() {
+        proxy = stop(proxy, DigestEchoServer.TunnelingProxy::stop);
+        http1Server = stop(http1Server, HttpTestServer::stop);
+        https1Server = stop(https1Server, HttpTestServer::stop);
+        http2Server = stop(http2Server, HttpTestServer::stop);
+        https2Server = stop(https2Server, HttpTestServer::stop);
+        client = null;
+        try {
+            executor.awaitTermination(2000, TimeUnit.MILLISECONDS);
+        } catch (Throwable x) {
+        } finally {
+            executor.shutdownNow();
+        }
+        try {
+            clientexec.awaitTermination(2000, TimeUnit.MILLISECONDS);
+        } catch (Throwable x) {
+        } finally {
+            clientexec.shutdownNow();
+        }
+        System.out.println("Teardown: done");
+    }
+
+    private interface Stoppable<T> {
+        public void stop(T service) throws Exception;
+    }
+
+    static <T> T stop(T service, Stoppable<T> stop) {
+        try {
+            if (service != null) stop.stop(service);
+        } catch (Throwable x) {
+        }
+        ;
+        return null;
+    }
+
+    static class HttpProxySelector extends ProxySelector {
+        private static final List<Proxy> NO_PROXY = List.of(Proxy.NO_PROXY);
+        private final List<Proxy> proxyList;
+
+        HttpProxySelector(InetSocketAddress proxyAddress) {
+            proxyList = List.of(new Proxy(Proxy.Type.HTTP, proxyAddress));
+        }
+
+        @Override
+        public List<Proxy> select(URI uri) {
+            // our proxy only supports tunneling
+            if (uri.getScheme().equalsIgnoreCase("https")) {
+                if (uri.getPath().contains("/proxy/")) {
+                    return proxyList;
+                }
+            }
+            return NO_PROXY;
+        }
+
+        @Override
+        public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+            System.err.println("Connection to proxy failed: " + ioe);
+            System.err.println("Proxy: " + sa);
+            System.err.println("\tURI: " + uri);
+            ioe.printStackTrace();
+        }
+    }
+
+    public static class HttpTestLargeHandler implements HttpTestHandler {
+        @Override
+        public void handle(HttpTestExchange t) throws IOException {
+            try (InputStream is = t.getRequestBody();
+                 OutputStream os = t.getResponseBody()) {
+                byte[] bytes = is.readAllBytes();
+                assert bytes.length == 0;
+                URI u = t.getRequestURI();
+                long responseID = Long.parseLong(u.getQuery().substring(2));
+                System.out.println("Server " + t.getRequestURI() + " sending response " + responseID);
+                t.sendResponseHeaders(200, DATA.length * 3);
+                for (int i = 0; i < 3; i++) {
+                    os.write(DATA);
+                }
+                System.out.println("\tresp:" + responseID + ": done");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/LargeResponseTest.java	Wed Oct 16 14:50:53 2019 +0100
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsServer;
+import jdk.test.lib.net.SimpleSSLContext;
+
+import javax.net.ssl.SSLContext;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @test
+ * @bug 8231449
+ * @summary This test verifies that the HttpClient works correctly when the server
+ *          sends large amount of data. Note that this test will pass even without
+ *          the fix for JDK-8231449, which is unfortunate.
+ * @library /test/lib http2/server
+ * @build jdk.test.lib.net.SimpleSSLContext HttpServerAdapters DigestEchoServer LargeResponseTest
+ * @modules java.net.http/jdk.internal.net.http.common
+ *          java.net.http/jdk.internal.net.http.frame
+ *          java.net.http/jdk.internal.net.http.hpack
+ *          java.logging
+ *          java.base/sun.net.www.http
+ *          java.base/sun.net.www
+ *          java.base/sun.net
+ * @run main/othervm -Dtest.requiresHost=true
+ *                   -Djdk.httpclient.HttpClient.log=headers
+ *                   -Djdk.internal.httpclient.debug=true
+ *                   LargeResponseTest
+ *
+ */
+public class LargeResponseTest implements HttpServerAdapters {
+    static final byte[] DATA;
+    static {
+        DATA = new byte[64 * 1024];
+        int len = 'z' - 'a';
+        for (int i=0; i < DATA.length; i++) {
+            DATA[i] = (byte) ('a' + (i % len));
+        }
+    }
+
+    static final SSLContext context;
+    static {
+        try {
+            context = new SimpleSSLContext().get();
+            SSLContext.setDefault(context);
+        } catch (Exception x) {
+            throw new ExceptionInInitializerError(x);
+        }
+    }
+
+    final AtomicLong requestCounter = new AtomicLong();
+    final AtomicLong responseCounter = new AtomicLong();
+    HttpTestServer http1Server;
+    HttpTestServer http2Server;
+    HttpTestServer https1Server;
+    HttpTestServer https2Server;
+    DigestEchoServer.TunnelingProxy proxy;
+
+    URI http1URI;
+    URI https1URI;
+    URI http2URI;
+    URI https2URI;
+    InetSocketAddress proxyAddress;
+    ProxySelector proxySelector;
+    HttpClient client;
+    List<CompletableFuture<?>>  futures = new CopyOnWriteArrayList<>();
+    Set<URI> pending = new CopyOnWriteArraySet<>();
+
+    final ExecutorService executor = new ThreadPoolExecutor(12, 60, 10,
+            TimeUnit.SECONDS, new LinkedBlockingQueue<>());
+    final ExecutorService clientexec = new ThreadPoolExecutor(6, 12, 1,
+            TimeUnit.SECONDS, new LinkedBlockingQueue<>());
+
+    public HttpClient newHttpClient(ProxySelector ps) {
+        HttpClient.Builder builder = HttpClient
+                .newBuilder()
+                .sslContext(context)
+                .executor(clientexec)
+                .proxy(ps);
+        return builder.build();
+    }
+
+    public void setUp() throws Exception {
+        try {
+            InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
+
+            // HTTP/1.1
+            HttpServer server1 = HttpServer.create(sa, 0);
+            server1.setExecutor(executor);
+            http1Server = HttpTestServer.of(server1);
+            http1Server.addHandler(new HttpTestLargeHandler(), "/LargeResponseTest/http1/");
+            http1Server.start();
+            http1URI = new URI("http://" + http1Server.serverAuthority() + "/LargeResponseTest/http1/");
+
+
+            // HTTPS/1.1
+            HttpsServer sserver1 = HttpsServer.create(sa, 100);
+            sserver1.setExecutor(executor);
+            sserver1.setHttpsConfigurator(new HttpsConfigurator(context));
+            https1Server = HttpTestServer.of(sserver1);
+            https1Server.addHandler(new HttpTestLargeHandler(), "/LargeResponseTest/https1/");
+            https1Server.start();
+            https1URI = new URI("https://" + https1Server.serverAuthority() + "/LargeResponseTest/https1/");
+
+            // HTTP/2.0
+            http2Server = HttpTestServer.of(
+                    new Http2TestServer("localhost", false, 0));
+            http2Server.addHandler(new HttpTestLargeHandler(), "/LargeResponseTest/http2/");
+            http2Server.start();
+            http2URI = new URI("http://" + http2Server.serverAuthority() + "/LargeResponseTest/http2/");
+
+            // HTTPS/2.0
+            https2Server = HttpTestServer.of(
+                    new Http2TestServer("localhost", true, 0));
+            https2Server.addHandler(new HttpTestLargeHandler(), "/LargeResponseTest/https2/");
+            https2Server.start();
+            https2URI = new URI("https://" + https2Server.serverAuthority() + "/LargeResponseTest/https2/");
+
+            proxy = DigestEchoServer.createHttpsProxyTunnel(
+                    DigestEchoServer.HttpAuthSchemeType.NONE);
+            proxyAddress = proxy.getProxyAddress();
+            proxySelector = new HttpProxySelector(proxyAddress);
+            client = newHttpClient(proxySelector);
+            System.out.println("Setup: done");
+        } catch (Exception x) {
+            tearDown(); throw x;
+        } catch (Error e) {
+            tearDown(); throw e;
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        LargeResponseTest test = new LargeResponseTest();
+        test.setUp();
+        long start = System.nanoTime();
+        try {
+            test.run(args);
+        } finally {
+            try {
+                long elapsed = System.nanoTime() - start;
+                System.out.println("*** Elapsed: " + Duration.ofNanos(elapsed));
+            } finally {
+                test.tearDown();
+            }
+        }
+    }
+
+    public void run(String... args) throws Exception {
+        List<URI> serverURIs = List.of(http1URI, http2URI, https1URI, https2URI);
+        for (int i=0; i<5; i++) {
+            for (URI base : serverURIs) {
+                if (base.getScheme().equalsIgnoreCase("https")) {
+                    URI proxy = i % 1 == 0 ? base.resolve(URI.create("proxy/foo?n="+requestCounter.incrementAndGet()))
+                    : base.resolve(URI.create("direct/foo?n="+requestCounter.incrementAndGet()));
+                    test(proxy);
+                }
+            }
+            for (URI base : serverURIs) {
+                URI direct = base.resolve(URI.create("direct/foo?n="+requestCounter.incrementAndGet()));
+                test(direct);
+            }
+        }
+        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
+    }
+
+    public void test(URI uri) throws Exception {
+        System.out.println("Testing with " + uri);
+        pending.add(uri);
+        HttpRequest request = HttpRequest.newBuilder(uri).build();
+        CompletableFuture<HttpResponse<String>> resp =
+                client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
+                .whenComplete((r, t) -> this.requestCompleted(request, r, t));
+        futures.add(resp);
+    }
+
+    private void requestCompleted(HttpRequest request, HttpResponse<?> r, Throwable t) {
+        responseCounter.incrementAndGet();
+        pending.remove(request.uri());
+        System.out.println(request + " -> " + (t == null ? r : t)
+                + " [still pending: " + (requestCounter.get() - responseCounter.get()) +"]");
+        if (pending.size() < 10 && requestCounter.get() > 10) {
+            pending.forEach(u -> System.out.println("\tpending: " + u));
+        }
+    }
+
+    public void tearDown() {
+        proxy = stop(proxy, DigestEchoServer.TunnelingProxy::stop);
+        http1Server = stop(http1Server, HttpTestServer::stop);
+        https1Server = stop(https1Server, HttpTestServer::stop);
+        http2Server = stop(http2Server, HttpTestServer::stop);
+        https2Server = stop(https2Server, HttpTestServer::stop);
+        client = null;
+        try {
+            executor.awaitTermination(2000, TimeUnit.MILLISECONDS);
+        } catch (Throwable x) {
+        } finally {
+            executor.shutdownNow();
+        }
+        try {
+            clientexec.awaitTermination(2000, TimeUnit.MILLISECONDS);
+        } catch (Throwable x) {
+        } finally {
+            clientexec.shutdownNow();
+        }
+        System.out.println("Teardown: done");
+    }
+
+    private interface Stoppable<T> { public void stop(T service) throws Exception; }
+
+    static <T>  T stop(T service, Stoppable<T> stop) {
+        try { if (service != null) stop.stop(service); } catch (Throwable x) { };
+        return null;
+    }
+
+    static class HttpProxySelector extends ProxySelector {
+        private static final List<Proxy> NO_PROXY = List.of(Proxy.NO_PROXY);
+        private final List<Proxy> proxyList;
+        HttpProxySelector(InetSocketAddress proxyAddress) {
+            proxyList = List.of(new Proxy(Proxy.Type.HTTP, proxyAddress));
+        }
+
+        @Override
+        public List<Proxy> select(URI uri) {
+            // our proxy only supports tunneling
+            if (uri.getScheme().equalsIgnoreCase("https")) {
+                if (uri.getPath().contains("/proxy/")) {
+                    return proxyList;
+                }
+            }
+            return NO_PROXY;
+        }
+
+        @Override
+        public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+            System.err.println("Connection to proxy failed: " + ioe);
+            System.err.println("Proxy: " + sa);
+            System.err.println("\tURI: " + uri);
+            ioe.printStackTrace();
+        }
+    }
+
+    public static class HttpTestLargeHandler implements HttpTestHandler {
+        @Override
+        public void handle(HttpTestExchange t) throws IOException {
+            try (InputStream is = t.getRequestBody();
+                 OutputStream os = t.getResponseBody()) {
+                byte[] bytes = is.readAllBytes();
+                assert bytes.length == 0;
+                URI u = t.getRequestURI();
+                long responseID = Long.parseLong(u.getQuery().substring(2));
+                System.out.println("Server " + t.getRequestURI() + " sending response " + responseID);
+                t.sendResponseHeaders(200, DATA.length * 3);
+                for (int i=0; i<3; i++) {
+                    os.write(DATA);
+                }
+                System.out.println("\tresp:" + responseID + ": done");
+            }
+        }
+    }
+
+}