Private
Public Access
- Remember the connection that worked and try it alone first next start - Prefer plain HTTP on the home network (each TLS handshake costs seconds on a 600 MHz phone); new setting "Encrypt at home too" to keep HTTPS - Try secure addresses one at a time instead of in parallel, so they don't all time out together - TLS 1.2 with session resumption: reconnects skip the full handshake - Cache certificate chains already verified; changing the trusted roots clears that cache and all TLS sessions - README: updated connection order; test: session resumption check and HandshakeProfile Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
50 lines
3.1 KiB
Java
50 lines
3.1 KiB
Java
import java.io.File;
|
|
import java.util.*;
|
|
import com.minamp.net.*;
|
|
|
|
/** Samples the handshake thread's stack to see where TLS time goes (run with -Xint). */
|
|
public class HandshakeProfile {
|
|
public static void main(String[] a) throws Exception {
|
|
CertVerifier.setRootStore(new PlexTest.RootDir(new File(a[0])));
|
|
final String url = a[1];
|
|
final int rounds = Integer.parseInt(a[2]);
|
|
final Map<String, Integer> buckets = new TreeMap<String, Integer>();
|
|
final long[] times = new long[rounds];
|
|
final Thread[] worker = new Thread[1];
|
|
worker[0] = Threads.create(new Runnable() { public void run() {
|
|
for (int i = 0; i < rounds; i++) {
|
|
Http.evictAll();
|
|
long t0 = System.nanoTime();
|
|
try { Http.get(url, null, 60000).readAll(); } catch (Exception e) { System.out.println(e); }
|
|
times[i] = (System.nanoTime() - t0) / 1000000;
|
|
}
|
|
}}, "hs");
|
|
worker[0].start();
|
|
while (worker[0].isAlive()) {
|
|
StackTraceElement[] st = worker[0].getStackTrace();
|
|
String b = "other";
|
|
for (StackTraceElement e : st) {
|
|
String c = e.getClassName() + "." + e.getMethodName();
|
|
if (c.startsWith("com.minamp.net.CertVerifier.signatureValid")) { b = "our chain check: signature math"; break; }
|
|
if (c.startsWith("com.minamp.net.CertVerifier")) { b = "our chain check: other"; break; }
|
|
if (c.contains("Ed25519") || c.contains("X25519") || c.contains("rfc7748")) { b = "X25519 key exchange"; break; }
|
|
if (c.contains("ECDSASigner") || c.contains("DSADigestSigner")) { b = "ECDSA verify (server signature)"; break; }
|
|
if (c.contains("RSA")) { b = "RSA"; break; }
|
|
if (c.contains("math.ec")) { b = "EC math (other)"; break; }
|
|
if (c.contains("asn1")) { b = "ASN.1 parsing"; break; }
|
|
if (c.contains("ChaCha") || c.contains("Poly1305") || c.contains("AES") || c.contains("GCM")) { b = "symmetric cipher"; break; }
|
|
if (c.contains("Digest") || c.contains("HMAC") || c.contains("HKDF")) { b = "hashing/HKDF"; break; }
|
|
if (c.contains("SecureRandom") || c.contains("Random")) { b = "random"; break; }
|
|
if (c.contains("java.net") || c.contains("SocketInputStream") || c.contains("NioSocket") || c.contains("read0")) { b = "network wait"; break; }
|
|
}
|
|
Integer n = buckets.get(b); buckets.put(b, n == null ? 1 : n + 1);
|
|
Thread.sleep(3);
|
|
}
|
|
int total = 0; for (int v : buckets.values()) total += v;
|
|
System.out.println("handshake ms: " + Arrays.toString(times));
|
|
List<Map.Entry<String, Integer>> l = new ArrayList<Map.Entry<String, Integer>>(buckets.entrySet());
|
|
Collections.sort(l, new Comparator<Map.Entry<String, Integer>>() { public int compare(Map.Entry<String, Integer> x, Map.Entry<String, Integer> y) { return y.getValue() - x.getValue(); } });
|
|
for (Map.Entry<String, Integer> e : l) System.out.printf(" %5.1f%% %s%n", 100.0 * e.getValue() / total, e.getKey());
|
|
}
|
|
}
|