Minamp 1.4: faster connecting

- 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>
This commit is contained in:
xockoandClaude Opus 5.5 committed 2026-10-14 21:16:09 +02:00
1 parent 58d21b6d68
commit 6fd17bc9c5
9 files changed
+284 -67

No files matched your search

+49
View File
@@ -0,0 +1,49 @@
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());
}
}